mirror of
https://github.com/verdaccio/verdaccio.git
synced 2025-02-17 03:19:36 +01:00
Refactor: Moved Search to Header (#1064)
* refactor: Moved Search to Header feat: added TxtField. WIP refactor: replaced downshift by react-autosuggest refactor: moved search's state refactor: introduced weight 300 & 500 refactor: changed container css margin refactor: made it more abstract refactor: replaced name by label. changed css refactor: removed aria refactor: removed margin fix: fixed flow types fix: fixed tags overlapping fix: fixed search. WIP refactor: removed useless library and added rect-router refactor: fixed tests * chore: remove tpm file * feat: added component Loading feat: added component Layout refactor: changed css refactor: added md prop refactor: moved Header back to App * chore: fix flow * fix: update snapshot fix: fixed componentDidMount parameter refactor: added onKeyDown event fix: fixed bad request refactor: renamed interfaces files refactor: refactor: logic display results refactor: changed minor things fix: fixed tests fix: fixed tests
This commit is contained in:
parent
98754c03a3
commit
9d265996f9
38
flow-typed/npm/@material-ui/core/InputAdornment_vx.x.x.js
vendored
Normal file
38
flow-typed/npm/@material-ui/core/InputAdornment_vx.x.x.js
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
// flow-typed signature: bc8a4aaaeb1e738c5006d06072a9b064
|
||||
// flow-typed version: <<STUB>>/@material-ui/core/InputAdornment_v3.1.0/flow_v0.81.0
|
||||
|
||||
/**
|
||||
* This is an autogenerated libdef stub for:
|
||||
*
|
||||
* '@material-ui/core/InputAdornment'
|
||||
*
|
||||
* Fill this stub out by replacing all the `any` types.
|
||||
*
|
||||
* Once filled out, we encourage you to share your work with the
|
||||
* community by sending a pull request to:
|
||||
* https://github.com/flowtype/flow-typed
|
||||
*/
|
||||
|
||||
declare module '@material-ui/core/InputAdornment' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* We include stubs for each file inside this npm package in case you need to
|
||||
* require those files directly. Feel free to delete any files that aren't
|
||||
* needed.
|
||||
*/
|
||||
declare module '@material-ui/core/InputAdornment/InputAdornment' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
// Filename aliases
|
||||
declare module '@material-ui/core/InputAdornment/index' {
|
||||
declare module.exports: $Exports<'@material-ui/core/InputAdornment'>;
|
||||
}
|
||||
declare module '@material-ui/core/InputAdornment/index.js' {
|
||||
declare module.exports: $Exports<'@material-ui/core/InputAdornment'>;
|
||||
}
|
||||
declare module '@material-ui/core/InputAdornment/InputAdornment.js' {
|
||||
declare module.exports: $Exports<'@material-ui/core/InputAdornment/InputAdornment'>;
|
||||
}
|
38
flow-typed/npm/@material-ui/core/MenuItem_vx.x.x.js
vendored
Normal file
38
flow-typed/npm/@material-ui/core/MenuItem_vx.x.x.js
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
// flow-typed signature: fd8dc668544eb744d5267a667187804b
|
||||
// flow-typed version: <<STUB>>/@material-ui/core/MenuItem_v3.1.0/flow_v0.81.0
|
||||
|
||||
/**
|
||||
* This is an autogenerated libdef stub for:
|
||||
*
|
||||
* '@material-ui/core/MenuItem'
|
||||
*
|
||||
* Fill this stub out by replacing all the `any` types.
|
||||
*
|
||||
* Once filled out, we encourage you to share your work with the
|
||||
* community by sending a pull request to:
|
||||
* https://github.com/flowtype/flow-typed
|
||||
*/
|
||||
|
||||
declare module '@material-ui/core/MenuItem' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* We include stubs for each file inside this npm package in case you need to
|
||||
* require those files directly. Feel free to delete any files that aren't
|
||||
* needed.
|
||||
*/
|
||||
declare module '@material-ui/core/MenuItem/MenuItem' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
// Filename aliases
|
||||
declare module '@material-ui/core/MenuItem/index' {
|
||||
declare module.exports: $Exports<'@material-ui/core/MenuItem'>;
|
||||
}
|
||||
declare module '@material-ui/core/MenuItem/index.js' {
|
||||
declare module.exports: $Exports<'@material-ui/core/MenuItem'>;
|
||||
}
|
||||
declare module '@material-ui/core/MenuItem/MenuItem.js' {
|
||||
declare module.exports: $Exports<'@material-ui/core/MenuItem/MenuItem'>;
|
||||
}
|
38
flow-typed/npm/@material-ui/core/Paper_vx.x.x.js
vendored
Normal file
38
flow-typed/npm/@material-ui/core/Paper_vx.x.x.js
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
// flow-typed signature: 1ac90635766a00f883f3d21d79c9f12e
|
||||
// flow-typed version: <<STUB>>/@material-ui/core/Paper_v3.1.0/flow_v0.81.0
|
||||
|
||||
/**
|
||||
* This is an autogenerated libdef stub for:
|
||||
*
|
||||
* '@material-ui/core/Paper'
|
||||
*
|
||||
* Fill this stub out by replacing all the `any` types.
|
||||
*
|
||||
* Once filled out, we encourage you to share your work with the
|
||||
* community by sending a pull request to:
|
||||
* https://github.com/flowtype/flow-typed
|
||||
*/
|
||||
|
||||
declare module '@material-ui/core/Paper' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* We include stubs for each file inside this npm package in case you need to
|
||||
* require those files directly. Feel free to delete any files that aren't
|
||||
* needed.
|
||||
*/
|
||||
declare module '@material-ui/core/Paper/Paper' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
// Filename aliases
|
||||
declare module '@material-ui/core/Paper/index' {
|
||||
declare module.exports: $Exports<'@material-ui/core/Paper'>;
|
||||
}
|
||||
declare module '@material-ui/core/Paper/index.js' {
|
||||
declare module.exports: $Exports<'@material-ui/core/Paper'>;
|
||||
}
|
||||
declare module '@material-ui/core/Paper/Paper.js' {
|
||||
declare module.exports: $Exports<'@material-ui/core/Paper/Paper'>;
|
||||
}
|
38
flow-typed/npm/@material-ui/core/TextField_vx.x.x.js
vendored
Normal file
38
flow-typed/npm/@material-ui/core/TextField_vx.x.x.js
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
// flow-typed signature: 864619754dd206242d851f1d47ddb63f
|
||||
// flow-typed version: <<STUB>>/@material-ui/core/TextField_v3.0.1/flow_v0.81.0
|
||||
|
||||
/**
|
||||
* This is an autogenerated libdef stub for:
|
||||
*
|
||||
* '@material-ui/core/TextField'
|
||||
*
|
||||
* Fill this stub out by replacing all the `any` types.
|
||||
*
|
||||
* Once filled out, we encourage you to share your work with the
|
||||
* community by sending a pull request to:
|
||||
* https://github.com/flowtype/flow-typed
|
||||
*/
|
||||
|
||||
declare module '@material-ui/core/TextField' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* We include stubs for each file inside this npm package in case you need to
|
||||
* require those files directly. Feel free to delete any files that aren't
|
||||
* needed.
|
||||
*/
|
||||
declare module '@material-ui/core/TextField/TextField' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
// Filename aliases
|
||||
declare module '@material-ui/core/TextField/index' {
|
||||
declare module.exports: $Exports<'@material-ui/core/TextField'>;
|
||||
}
|
||||
declare module '@material-ui/core/TextField/index.js' {
|
||||
declare module.exports: $Exports<'@material-ui/core/TextField'>;
|
||||
}
|
||||
declare module '@material-ui/core/TextField/TextField.js' {
|
||||
declare module.exports: $Exports<'@material-ui/core/TextField/TextField'>;
|
||||
}
|
4
flow-typed/npm/@material-ui/core_vx.x.x.js
vendored
4
flow-typed/npm/@material-ui/core_vx.x.x.js
vendored
@ -1,5 +1,5 @@
|
||||
// flow-typed signature: 55ef97a6a933779dac4db73eb9147735
|
||||
// flow-typed version: <<STUB>>/@material-ui/core_v3.x.x/flow_v0.77.0
|
||||
// flow-typed signature: 412328dad707658ee9e7ff8346800ab2
|
||||
// flow-typed version: <<STUB>>/@material-ui/core_v3.0.1/flow_v0.81.0
|
||||
|
||||
/**
|
||||
* This is an autogenerated libdef stub for:
|
||||
|
4
flow-typed/npm/@material-ui/icons_vx.x.x.js
vendored
4
flow-typed/npm/@material-ui/icons_vx.x.x.js
vendored
@ -1,5 +1,5 @@
|
||||
// flow-typed signature: d51b6caa61b0a7c4c66d42853d664055
|
||||
// flow-typed version: <<STUB>>/@material-ui/icons_v3.x.x/flow_v0.77.0
|
||||
// flow-typed signature: 59e1686415435e183c7c98de573c90d5
|
||||
// flow-typed version: <<STUB>>/@material-ui/icons_v3.0.1/flow_v0.81.0
|
||||
|
||||
/**
|
||||
* This is an autogenerated libdef stub for:
|
||||
|
33
flow-typed/npm/autosuggest-highlight/match_vx.x.x.js
vendored
Normal file
33
flow-typed/npm/autosuggest-highlight/match_vx.x.x.js
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
// flow-typed signature: f4ce515b9395f4f0279d388b18ef59b5
|
||||
// flow-typed version: <<STUB>>/autosuggest-highlight/match_v3.1.1/flow_v0.81.0
|
||||
|
||||
/**
|
||||
* This is an autogenerated libdef stub for:
|
||||
*
|
||||
* 'autosuggest-highlight/match'
|
||||
*
|
||||
* Fill this stub out by replacing all the `any` types.
|
||||
*
|
||||
* Once filled out, we encourage you to share your work with the
|
||||
* community by sending a pull request to:
|
||||
* https://github.com/flowtype/flow-typed
|
||||
*/
|
||||
|
||||
declare module 'autosuggest-highlight/match' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* We include stubs for each file inside this npm package in case you need to
|
||||
* require those files directly. Feel free to delete any files that aren't
|
||||
* needed.
|
||||
*/
|
||||
|
||||
|
||||
// Filename aliases
|
||||
declare module 'autosuggest-highlight/match/index' {
|
||||
declare module.exports: $Exports<'autosuggest-highlight/match'>;
|
||||
}
|
||||
declare module 'autosuggest-highlight/match/index.js' {
|
||||
declare module.exports: $Exports<'autosuggest-highlight/match'>;
|
||||
}
|
33
flow-typed/npm/autosuggest-highlight/parse_vx.x.x.js
vendored
Normal file
33
flow-typed/npm/autosuggest-highlight/parse_vx.x.x.js
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
// flow-typed signature: 7df3e3914baffd57187e87617a708990
|
||||
// flow-typed version: <<STUB>>/autosuggest-highlight/parse_v3.1.1/flow_v0.81.0
|
||||
|
||||
/**
|
||||
* This is an autogenerated libdef stub for:
|
||||
*
|
||||
* 'autosuggest-highlight/parse'
|
||||
*
|
||||
* Fill this stub out by replacing all the `any` types.
|
||||
*
|
||||
* Once filled out, we encourage you to share your work with the
|
||||
* community by sending a pull request to:
|
||||
* https://github.com/flowtype/flow-typed
|
||||
*/
|
||||
|
||||
declare module 'autosuggest-highlight/parse' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* We include stubs for each file inside this npm package in case you need to
|
||||
* require those files directly. Feel free to delete any files that aren't
|
||||
* needed.
|
||||
*/
|
||||
|
||||
|
||||
// Filename aliases
|
||||
declare module 'autosuggest-highlight/parse/index' {
|
||||
declare module.exports: $Exports<'autosuggest-highlight/parse'>;
|
||||
}
|
||||
declare module 'autosuggest-highlight/parse/index.js' {
|
||||
declare module.exports: $Exports<'autosuggest-highlight/parse'>;
|
||||
}
|
60
flow-typed/npm/react-autosuggest_vx.x.x.js
vendored
Normal file
60
flow-typed/npm/react-autosuggest_vx.x.x.js
vendored
Normal file
@ -0,0 +1,60 @@
|
||||
// flow-typed signature: 844045a071365b8f4e9d7d1aac302959
|
||||
// flow-typed version: <<STUB>>/react-autosuggest_v9.4.2/flow_v0.81.0
|
||||
|
||||
/**
|
||||
* This is an autogenerated libdef stub for:
|
||||
*
|
||||
* 'react-autosuggest'
|
||||
*
|
||||
* Fill this stub out by replacing all the `any` types.
|
||||
*
|
||||
* Once filled out, we encourage you to share your work with the
|
||||
* community by sending a pull request to:
|
||||
* https://github.com/flowtype/flow-typed
|
||||
*/
|
||||
|
||||
declare module 'react-autosuggest' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* We include stubs for each file inside this npm package in case you need to
|
||||
* require those files directly. Feel free to delete any files that aren't
|
||||
* needed.
|
||||
*/
|
||||
declare module 'react-autosuggest/dist/Autosuggest' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-autosuggest/dist/index' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-autosuggest/dist/standalone/autosuggest' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-autosuggest/dist/standalone/autosuggest.min' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-autosuggest/dist/theme' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
// Filename aliases
|
||||
declare module 'react-autosuggest/dist/Autosuggest.js' {
|
||||
declare module.exports: $Exports<'react-autosuggest/dist/Autosuggest'>;
|
||||
}
|
||||
declare module 'react-autosuggest/dist/index.js' {
|
||||
declare module.exports: $Exports<'react-autosuggest/dist/index'>;
|
||||
}
|
||||
declare module 'react-autosuggest/dist/standalone/autosuggest.js' {
|
||||
declare module.exports: $Exports<'react-autosuggest/dist/standalone/autosuggest'>;
|
||||
}
|
||||
declare module 'react-autosuggest/dist/standalone/autosuggest.min.js' {
|
||||
declare module.exports: $Exports<'react-autosuggest/dist/standalone/autosuggest.min'>;
|
||||
}
|
||||
declare module 'react-autosuggest/dist/theme.js' {
|
||||
declare module.exports: $Exports<'react-autosuggest/dist/theme'>;
|
||||
}
|
199
flow-typed/npm/react-router_vx.x.x.js
vendored
Normal file
199
flow-typed/npm/react-router_vx.x.x.js
vendored
Normal file
@ -0,0 +1,199 @@
|
||||
// flow-typed signature: 4fb3dfe55b5d1711432e74df5fa80adc
|
||||
// flow-typed version: <<STUB>>/react-router_v4.3.1/flow_v0.81.0
|
||||
|
||||
/**
|
||||
* This is an autogenerated libdef stub for:
|
||||
*
|
||||
* 'react-router'
|
||||
*
|
||||
* Fill this stub out by replacing all the `any` types.
|
||||
*
|
||||
* Once filled out, we encourage you to share your work with the
|
||||
* community by sending a pull request to:
|
||||
* https://github.com/flowtype/flow-typed
|
||||
*/
|
||||
|
||||
declare module 'react-router' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* We include stubs for each file inside this npm package in case you need to
|
||||
* require those files directly. Feel free to delete any files that aren't
|
||||
* needed.
|
||||
*/
|
||||
declare module 'react-router/es/generatePath' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-router/es/index' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-router/es/matchPath' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-router/es/MemoryRouter' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-router/es/Prompt' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-router/es/Redirect' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-router/es/Route' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-router/es/Router' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-router/es/RouterContext' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-router/es/StaticRouter' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-router/es/Switch' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-router/es/withRouter' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-router/generatePath' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-router/matchPath' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-router/MemoryRouter' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-router/Prompt' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-router/Redirect' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-router/Route' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-router/Router' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-router/StaticRouter' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-router/Switch' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-router/umd/react-router' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-router/umd/react-router.min' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-router/withRouter' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
// Filename aliases
|
||||
declare module 'react-router/es/generatePath.js' {
|
||||
declare module.exports: $Exports<'react-router/es/generatePath'>;
|
||||
}
|
||||
declare module 'react-router/es/index.js' {
|
||||
declare module.exports: $Exports<'react-router/es/index'>;
|
||||
}
|
||||
declare module 'react-router/es/matchPath.js' {
|
||||
declare module.exports: $Exports<'react-router/es/matchPath'>;
|
||||
}
|
||||
declare module 'react-router/es/MemoryRouter.js' {
|
||||
declare module.exports: $Exports<'react-router/es/MemoryRouter'>;
|
||||
}
|
||||
declare module 'react-router/es/Prompt.js' {
|
||||
declare module.exports: $Exports<'react-router/es/Prompt'>;
|
||||
}
|
||||
declare module 'react-router/es/Redirect.js' {
|
||||
declare module.exports: $Exports<'react-router/es/Redirect'>;
|
||||
}
|
||||
declare module 'react-router/es/Route.js' {
|
||||
declare module.exports: $Exports<'react-router/es/Route'>;
|
||||
}
|
||||
declare module 'react-router/es/Router.js' {
|
||||
declare module.exports: $Exports<'react-router/es/Router'>;
|
||||
}
|
||||
declare module 'react-router/es/RouterContext.js' {
|
||||
declare module.exports: $Exports<'react-router/es/RouterContext'>;
|
||||
}
|
||||
declare module 'react-router/es/StaticRouter.js' {
|
||||
declare module.exports: $Exports<'react-router/es/StaticRouter'>;
|
||||
}
|
||||
declare module 'react-router/es/Switch.js' {
|
||||
declare module.exports: $Exports<'react-router/es/Switch'>;
|
||||
}
|
||||
declare module 'react-router/es/withRouter.js' {
|
||||
declare module.exports: $Exports<'react-router/es/withRouter'>;
|
||||
}
|
||||
declare module 'react-router/generatePath.js' {
|
||||
declare module.exports: $Exports<'react-router/generatePath'>;
|
||||
}
|
||||
declare module 'react-router/index' {
|
||||
declare module.exports: $Exports<'react-router'>;
|
||||
}
|
||||
declare module 'react-router/index.js' {
|
||||
declare module.exports: $Exports<'react-router'>;
|
||||
}
|
||||
declare module 'react-router/matchPath.js' {
|
||||
declare module.exports: $Exports<'react-router/matchPath'>;
|
||||
}
|
||||
declare module 'react-router/MemoryRouter.js' {
|
||||
declare module.exports: $Exports<'react-router/MemoryRouter'>;
|
||||
}
|
||||
declare module 'react-router/Prompt.js' {
|
||||
declare module.exports: $Exports<'react-router/Prompt'>;
|
||||
}
|
||||
declare module 'react-router/Redirect.js' {
|
||||
declare module.exports: $Exports<'react-router/Redirect'>;
|
||||
}
|
||||
declare module 'react-router/Route.js' {
|
||||
declare module.exports: $Exports<'react-router/Route'>;
|
||||
}
|
||||
declare module 'react-router/Router.js' {
|
||||
declare module.exports: $Exports<'react-router/Router'>;
|
||||
}
|
||||
declare module 'react-router/StaticRouter.js' {
|
||||
declare module.exports: $Exports<'react-router/StaticRouter'>;
|
||||
}
|
||||
declare module 'react-router/Switch.js' {
|
||||
declare module.exports: $Exports<'react-router/Switch'>;
|
||||
}
|
||||
declare module 'react-router/umd/react-router.js' {
|
||||
declare module.exports: $Exports<'react-router/umd/react-router'>;
|
||||
}
|
||||
declare module 'react-router/umd/react-router.min.js' {
|
||||
declare module.exports: $Exports<'react-router/umd/react-router.min'>;
|
||||
}
|
||||
declare module 'react-router/withRouter.js' {
|
||||
declare module.exports: $Exports<'react-router/withRouter'>;
|
||||
}
|
@ -20,6 +20,7 @@
|
||||
"@verdaccio/streams": "1.0.0",
|
||||
"JSONStream": "1.3.4",
|
||||
"async": "2.6.1",
|
||||
"autosuggest-highlight": "3.1.1",
|
||||
"body-parser": "1.18.3",
|
||||
"bunyan": "1.8.12",
|
||||
"chalk": "2.4.1",
|
||||
@ -45,6 +46,8 @@
|
||||
"mkdirp": "0.5.1",
|
||||
"mv": "2.1.1",
|
||||
"pkginfo": "0.4.1",
|
||||
"react-autosuggest": "9.4.2",
|
||||
"react-router": "4.3.1",
|
||||
"request": "2.88.0",
|
||||
"semver": "5.5.1",
|
||||
"verdaccio-audit": "1.0.0",
|
||||
@ -85,7 +88,6 @@
|
||||
"codecov": "3.1.0",
|
||||
"cross-env": "5.2.0",
|
||||
"css-loader": "0.28.10",
|
||||
"element-theme-default": "1.4.13",
|
||||
"emotion": "9.2.8",
|
||||
"enzyme": "3.6.0",
|
||||
"enzyme-adapter-react-16": "1.5.0",
|
||||
|
320
src/webui/app.js
320
src/webui/app.js
@ -1,18 +1,28 @@
|
||||
import React, { Component } from 'react';
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import isNil from 'lodash/isNil';
|
||||
import 'element-theme-default';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import Dialog from '@material-ui/core/Dialog';
|
||||
import DialogActions from '@material-ui/core/DialogActions';
|
||||
import DialogContent from '@material-ui/core/DialogContent';
|
||||
import DialogTitle from '@material-ui/core/DialogTitle';
|
||||
import SnackbarContent from '@material-ui/core/SnackbarContent';
|
||||
import ErrorIcon from '@material-ui/icons/Error';
|
||||
|
||||
import storage from './utils/storage';
|
||||
import logo from './utils/logo';
|
||||
import { makeLogin, isTokenExpire } from './utils/login';
|
||||
|
||||
import Header from './components/Header';
|
||||
import Footer from './components/Footer';
|
||||
import Loading from './components/Loading';
|
||||
import LoginModal from './components/Login';
|
||||
|
||||
import Header from './components/Header';
|
||||
import { Container, Content } from './components/Layout';
|
||||
import Route from './router';
|
||||
import API from './utils/api';
|
||||
import { getDetailPageURL } from './utils/url';
|
||||
|
||||
import './styles/main.scss';
|
||||
import classes from "./app.scss";
|
||||
import 'normalize.css';
|
||||
|
||||
export default class App extends Component {
|
||||
@ -22,30 +32,46 @@ export default class App extends Component {
|
||||
user: {},
|
||||
scope: (window.VERDACCIO_SCOPE) ? `${window.VERDACCIO_SCOPE}:` : '',
|
||||
showLoginModal: false,
|
||||
isUserLoggedIn: false
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.handleLogout = this.handleLogout.bind(this);
|
||||
this.toggleLoginModal = this.toggleLoginModal.bind(this);
|
||||
this.doLogin = this.doLogin.bind(this);
|
||||
this.loadLogo = this.loadLogo.bind(this);
|
||||
this.isUserAlreadyLoggedIn = this.isUserAlreadyLoggedIn.bind(this);
|
||||
isUserLoggedIn: false,
|
||||
packages: [],
|
||||
searchPackages: [],
|
||||
filteredPackages: [],
|
||||
search: '',
|
||||
isLoading: true,
|
||||
showAlertDialog: false,
|
||||
alertDialogContent: {
|
||||
title: '',
|
||||
message: '',
|
||||
packages: []
|
||||
},
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.loadLogo();
|
||||
this.isUserAlreadyLoggedIn();
|
||||
this.loadPackages();
|
||||
}
|
||||
|
||||
isUserAlreadyLoggedIn() {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
componentDidUpdate(_, prevState) {
|
||||
if (prevState.isUserLoggedIn !== this.state.isUserLoggedIn) {
|
||||
this.loadPackages();
|
||||
}
|
||||
}
|
||||
|
||||
loadLogo = async () => {
|
||||
const logoUrl = await logo();
|
||||
this.setState({
|
||||
logoUrl
|
||||
});
|
||||
}
|
||||
|
||||
isUserAlreadyLoggedIn = () => {
|
||||
// checks for token validity
|
||||
const token = storage.getItem('token');
|
||||
const username = storage.getItem('username');
|
||||
|
||||
if (isTokenExpire(token) || isNil(username)) {
|
||||
this.handleLogout();
|
||||
this.handleLogout();
|
||||
} else {
|
||||
this.setState({
|
||||
user: { username, token },
|
||||
@ -54,16 +80,38 @@ export default class App extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
async loadLogo() {
|
||||
const logoUrl = await logo();
|
||||
this.setState({ logoUrl });
|
||||
loadPackages = async () => {
|
||||
try {
|
||||
this.req = await API.request('packages', 'GET');
|
||||
const transformedPackages = this.req.map(({ name, ...others}) => ({
|
||||
label: name,
|
||||
...others
|
||||
}));
|
||||
this.setState({
|
||||
packages: transformedPackages,
|
||||
filteredPackages: transformedPackages,
|
||||
isLoading: false
|
||||
});
|
||||
} catch (error) {
|
||||
this.handleShowAlertDialog({
|
||||
title: 'Warning',
|
||||
message: `Unable to load package list: ${error.message}`
|
||||
});
|
||||
this.setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
setLoading = isLoading => (
|
||||
this.setState({
|
||||
isLoading
|
||||
})
|
||||
)
|
||||
|
||||
/**
|
||||
* Toggles the login modal
|
||||
* Required by: <LoginModal /> <Header />
|
||||
*/
|
||||
toggleLoginModal() {
|
||||
toggleLoginModal = () => {
|
||||
this.setState((prevState) => ({
|
||||
showLoginModal: !prevState.showLoginModal,
|
||||
error: {}
|
||||
@ -74,27 +122,16 @@ export default class App extends Component {
|
||||
* handles login
|
||||
* Required by: <Header />
|
||||
*/
|
||||
async doLogin(usernameValue, passwordValue) {
|
||||
doLogin = async (usernameValue, passwordValue) => {
|
||||
const { username, token, error } = await makeLogin(
|
||||
usernameValue,
|
||||
passwordValue
|
||||
);
|
||||
|
||||
if (username && token) {
|
||||
this.setState({
|
||||
user: {
|
||||
username,
|
||||
token
|
||||
}
|
||||
});
|
||||
this.setLoggedUser(username, token);
|
||||
storage.setItem('username', username);
|
||||
storage.setItem('token', token);
|
||||
// close login modal after successful login
|
||||
// set isUserLoggedin to true
|
||||
this.setState({
|
||||
isUserLoggedIn: true,
|
||||
showLoginModal: false
|
||||
});
|
||||
}
|
||||
|
||||
if (error) {
|
||||
@ -105,11 +142,43 @@ export default class App extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
setLoggedUser = (username, token) => {
|
||||
this.setState({
|
||||
user: {
|
||||
username,
|
||||
token,
|
||||
},
|
||||
isUserLoggedIn: true, // close login modal after successful login
|
||||
showLoginModal: false // set isUserLoggedin to true
|
||||
});
|
||||
}
|
||||
|
||||
handleFetchPackages = async ({ value }) => {
|
||||
try {
|
||||
this.req = await API.request(`/search/${encodeURIComponent(value)}`, 'GET');
|
||||
const transformedPackages = this.req.map(({ name, ...others}) => ({
|
||||
label: name,
|
||||
...others
|
||||
}));
|
||||
// Implement cancel feature later
|
||||
if (this.state.search === value) {
|
||||
this.setState({
|
||||
searchPackages: transformedPackages
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
this.handleShowAlertDialog({
|
||||
title: 'Warning',
|
||||
message: `Unable to get search result: ${error.message}`
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logouts user
|
||||
* Required by: <Header />
|
||||
*/
|
||||
handleLogout() {
|
||||
handleLogout = () => {
|
||||
storage.removeItem('username');
|
||||
storage.removeItem('token');
|
||||
this.setState({
|
||||
@ -118,48 +187,159 @@ export default class App extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
renderHeader() {
|
||||
const {
|
||||
logoUrl,
|
||||
user,
|
||||
scope,
|
||||
} = this.state;
|
||||
return <Header
|
||||
logo={logoUrl}
|
||||
username={user.username}
|
||||
scope={scope}
|
||||
toggleLoginModal={this.toggleLoginModal}
|
||||
handleLogout={this.handleLogout}
|
||||
/>;
|
||||
handlePackagesClearRequested = () => {
|
||||
this.setState({
|
||||
searchPackages: []
|
||||
});
|
||||
};
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
handleSearch = (_, { newValue }) => {
|
||||
const { filteredPackages, packages, search } = this.state;
|
||||
const value = newValue.trim();
|
||||
this.setState({
|
||||
search: value,
|
||||
filteredPackages: value.length < search.length ?
|
||||
packages.filter(pkg => pkg.label.match(value)) : filteredPackages
|
||||
});
|
||||
};
|
||||
|
||||
handleKeyDown = event => {
|
||||
if (event.key === 'Enter') {
|
||||
const { filteredPackages, packages } = this.state;
|
||||
const value = event.target.value.trim();
|
||||
this.setState({
|
||||
filteredPackages: value ?
|
||||
packages.filter(pkg => pkg.label.match(value)) : filteredPackages
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderLoginModal() {
|
||||
const {
|
||||
error,
|
||||
showLoginModal
|
||||
} = this.state;
|
||||
return <LoginModal
|
||||
visibility={showLoginModal}
|
||||
error={error}
|
||||
onChange={this.setUsernameAndPassword}
|
||||
onCancel={this.toggleLoginModal}
|
||||
onSubmit={this.doLogin}
|
||||
/>;
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
handleClickSearch = (_, { suggestionValue, method }) => {
|
||||
const { packages } = this.state;
|
||||
switch(method) {
|
||||
case 'click':
|
||||
window.location.href = getDetailPageURL(suggestionValue);
|
||||
break;
|
||||
case 'enter':
|
||||
this.setState({
|
||||
filteredPackages: packages.filter(pkg => pkg.label.match(suggestionValue))
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
handleShowAlertDialog = content => {
|
||||
this.setState({
|
||||
showAlertDialog: true,
|
||||
alertDialogContent: content
|
||||
});
|
||||
}
|
||||
|
||||
handleDismissAlertDialog = () => {
|
||||
this.setState({
|
||||
showAlertDialog: false
|
||||
});
|
||||
};
|
||||
|
||||
getfilteredPackages = value => {
|
||||
const inputValue = value.trim().toLowerCase();
|
||||
const inputLength = inputValue.length;
|
||||
|
||||
if (inputLength === 0) {
|
||||
return [];
|
||||
} else {
|
||||
return this.searchPackage(value);
|
||||
}
|
||||
}
|
||||
|
||||
renderHeader = () => {
|
||||
const { logoUrl, user, search, searchPackages } = this.state;
|
||||
return (
|
||||
<Header
|
||||
logo={logoUrl}
|
||||
username={user.username}
|
||||
toggleLoginModal={this.toggleLoginModal}
|
||||
onLogout={this.handleLogout}
|
||||
onSearch={this.handleSearch}
|
||||
onSuggestionsFetch={this.handleFetchPackages}
|
||||
onCleanSuggestions={this.handlePackagesClearRequested}
|
||||
onClick={this.handleClickSearch}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
packages={searchPackages}
|
||||
search={search}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
renderAlertDialog = () => (
|
||||
<Dialog
|
||||
open={this.state.showAlertDialog}
|
||||
onClose={this.handleDismissAlertDialog}
|
||||
>
|
||||
<DialogTitle id="alert-dialog-title">
|
||||
{this.state.alertDialogContent.title}
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<SnackbarContent
|
||||
className={classes.alertError}
|
||||
message={
|
||||
<div
|
||||
id="client-snackbar"
|
||||
className={classes.alertErrorMsg}
|
||||
>
|
||||
<ErrorIcon className={classes.alertIcon} />
|
||||
<span>
|
||||
{this.state.alertDialogContent.message}
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button
|
||||
onClick={this.handleDismissAlertDialog}
|
||||
color="primary"
|
||||
autoFocus
|
||||
>
|
||||
Ok
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
)
|
||||
|
||||
renderLoginModal = () => {
|
||||
const { error, showLoginModal } = this.state;
|
||||
return (
|
||||
<LoginModal
|
||||
visibility={showLoginModal}
|
||||
error={error}
|
||||
onChange={this.setUsernameAndPassword}
|
||||
onCancel={this.toggleLoginModal}
|
||||
onSubmit={this.doLogin}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { isUserLoggedIn } = this.state;
|
||||
const { isLoading, ...others } = this.state;
|
||||
return (
|
||||
<div className="page-full-height">
|
||||
<div id="header">
|
||||
{this.renderHeader()}
|
||||
</div>
|
||||
<Container isLoading={isLoading}>
|
||||
{isLoading ? (
|
||||
<Loading />
|
||||
) : (
|
||||
<Fragment>
|
||||
{this.renderHeader()}
|
||||
<Content>
|
||||
<Route {...others} />
|
||||
</Content>
|
||||
<Footer />
|
||||
</Fragment>
|
||||
)}
|
||||
{this.renderAlertDialog()}
|
||||
{this.renderLoginModal()}
|
||||
<Route isUserLoggedIn={isUserLoggedIn} />
|
||||
<div id="footer">
|
||||
<Footer />
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
16
src/webui/app.scss
Normal file
16
src/webui/app.scss
Normal file
@ -0,0 +1,16 @@
|
||||
@import './styles/variables';
|
||||
|
||||
.alertError {
|
||||
background-color: $red !important;
|
||||
min-width: inherit !important;
|
||||
}
|
||||
|
||||
.alertErrorMsg {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.alertIcon {
|
||||
opacity: 0.9;
|
||||
margin-right: 8px;
|
||||
}
|
105
src/webui/components/AutoComplete/index.js
Normal file
105
src/webui/components/AutoComplete/index.js
Normal file
@ -0,0 +1,105 @@
|
||||
/**
|
||||
* @prettier
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import type { Node } from 'react';
|
||||
import Autosuggest from 'react-autosuggest';
|
||||
import match from 'autosuggest-highlight/match';
|
||||
import parse from 'autosuggest-highlight/parse';
|
||||
import Paper from '@material-ui/core/Paper';
|
||||
import MenuItem from '@material-ui/core/MenuItem';
|
||||
|
||||
import { fontWeight } from '../../utils/styles/sizes';
|
||||
import { Wrapper, InputField } from './styles';
|
||||
import { IProps } from './types';
|
||||
|
||||
const renderInputComponent = (inputProps): Node => {
|
||||
const { ref, startAdornment, disableUnderline, onKeyDown, ...others } = inputProps;
|
||||
return (
|
||||
<InputField
|
||||
fullWidth
|
||||
InputProps={{
|
||||
inputRef: node => {
|
||||
ref(node);
|
||||
},
|
||||
startAdornment,
|
||||
disableUnderline,
|
||||
onKeyDown,
|
||||
}}
|
||||
{...others}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const getSuggestionValue = (suggestion): string => suggestion.label;
|
||||
|
||||
const renderSuggestion = (suggestion, { query, isHighlighted }): Node => {
|
||||
const matches = match(suggestion.label, query);
|
||||
const parts = parse(suggestion.label, matches);
|
||||
return (
|
||||
<MenuItem selected={isHighlighted} component="div">
|
||||
<div>
|
||||
{parts.map((part, index) => {
|
||||
return part.highlight ? (
|
||||
<span key={String(index)} href={suggestion.link} style={{ fontWeight: fontWeight.semiBold }}>
|
||||
{part.text}
|
||||
</span>
|
||||
) : (
|
||||
<span key={String(index)} href={suggestion.link} style={{ fontWeight: fontWeight.light }}>
|
||||
{part.text}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</MenuItem>
|
||||
);
|
||||
};
|
||||
|
||||
const AutoComplete = ({
|
||||
suggestions,
|
||||
startAdornment,
|
||||
onChange,
|
||||
onSuggestionsFetch,
|
||||
onCleanSuggestions,
|
||||
value = '',
|
||||
placeholder = '',
|
||||
disableUnderline = false,
|
||||
color,
|
||||
onClick,
|
||||
onKeyDown,
|
||||
}: IProps): Node => {
|
||||
const autosuggestProps = {
|
||||
renderInputComponent,
|
||||
suggestions,
|
||||
getSuggestionValue,
|
||||
renderSuggestion,
|
||||
onSuggestionsFetchRequested: onSuggestionsFetch,
|
||||
onSuggestionsClearRequested: onCleanSuggestions,
|
||||
};
|
||||
return (
|
||||
<Wrapper>
|
||||
<Autosuggest
|
||||
{...autosuggestProps}
|
||||
inputProps={{
|
||||
value,
|
||||
onChange,
|
||||
placeholder,
|
||||
startAdornment,
|
||||
disableUnderline,
|
||||
color,
|
||||
onKeyDown,
|
||||
}}
|
||||
renderSuggestionsContainer={options => (
|
||||
<Paper {...options.containerProps} square>
|
||||
{options.children}
|
||||
</Paper>
|
||||
)}
|
||||
onSuggestionSelected={onClick}
|
||||
/>
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default AutoComplete;
|
52
src/webui/components/AutoComplete/styles.js
Normal file
52
src/webui/components/AutoComplete/styles.js
Normal file
@ -0,0 +1,52 @@
|
||||
/**
|
||||
* @prettier
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import styled, { css } from 'react-emotion';
|
||||
|
||||
import TxtField from '../TxtField';
|
||||
import { IInputField } from './types';
|
||||
|
||||
export const Wrapper = styled.div`
|
||||
&& {
|
||||
width: 100%;
|
||||
height: 32px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
`;
|
||||
|
||||
export const InputField = ({ color, ...others }: IInputField) => (
|
||||
<TxtField
|
||||
{...others}
|
||||
classes={{
|
||||
input: css`
|
||||
&& {
|
||||
${color &&
|
||||
css`
|
||||
color: ${color};
|
||||
`};
|
||||
}
|
||||
`,
|
||||
root: css`
|
||||
&& {
|
||||
&:before {
|
||||
content: '';
|
||||
border: none;
|
||||
}
|
||||
&:after {
|
||||
${color &&
|
||||
css`
|
||||
border-color: ${color};
|
||||
`};
|
||||
}
|
||||
&:hover:before {
|
||||
content: none;
|
||||
}
|
||||
}
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
);
|
24
src/webui/components/AutoComplete/types.js
Normal file
24
src/webui/components/AutoComplete/types.js
Normal file
@ -0,0 +1,24 @@
|
||||
/**
|
||||
* @prettier
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import { InputAdornmentProps } from '@material-ui/core/InputAdornment';
|
||||
|
||||
export interface IProps {
|
||||
suggestions: any[];
|
||||
color?: string;
|
||||
value?: string;
|
||||
placeholder?: string;
|
||||
startAdornment?: React.ComponentType<InputAdornmentProps>;
|
||||
disableUnderline?: boolean;
|
||||
onChange?: (event: SyntheticKeyboardEvent<HTMLInputElement>) => void;
|
||||
onSuggestionsFetch?: (event: SyntheticKeyboardEvent<HTMLInputElement>) => void;
|
||||
onCleanSuggestions?: () => void;
|
||||
onClick?: () => void;
|
||||
onKeyDown?: (event: SyntheticKeyboardEvent<HTMLInputElement>) => void;
|
||||
}
|
||||
|
||||
export interface IInputField {
|
||||
color: string;
|
||||
}
|
@ -8,7 +8,7 @@ import FileCopy from '@material-ui/icons/FileCopy';
|
||||
import Tooltip from '@material-ui/core/Tooltip/index';
|
||||
|
||||
import type { Node } from 'react';
|
||||
import { IProps } from './interfaces';
|
||||
import { IProps } from './types';
|
||||
|
||||
import { ClipBoardCopy, ClipBoardCopyText, CopyIcon } from './styles';
|
||||
|
||||
@ -33,7 +33,7 @@ const CopyToClipBoard = ({ text }: IProps): Node => (
|
||||
<ClipBoardCopy>
|
||||
<ClipBoardCopyText>{text}</ClipBoardCopyText>
|
||||
<Tooltip title="Copy to Clipboard" disableFocusListener>
|
||||
<CopyIcon aria-label="Copy to Clipboard" onClick={copyToClipBoardUtility(text)}>
|
||||
<CopyIcon onClick={copyToClipBoardUtility(text)}>
|
||||
<FileCopy />
|
||||
</CopyIcon>
|
||||
</Tooltip>
|
||||
|
0
src/webui/components/CopyToClipBoard/interfaces.js → src/webui/components/CopyToClipBoard/types.js
0
src/webui/components/CopyToClipBoard/interfaces.js → src/webui/components/CopyToClipBoard/types.js
@ -12,17 +12,21 @@ import Info from '@material-ui/icons/Info';
|
||||
import Help from '@material-ui/icons/Help';
|
||||
import Tooltip from '@material-ui/core/Tooltip/index';
|
||||
import AccountCircle from '@material-ui/icons/AccountCircle';
|
||||
import InputAdornment from '@material-ui/core/InputAdornment';
|
||||
import { default as IconSearch } from '@material-ui/icons/Search';
|
||||
|
||||
import { getRegistryURL } from '../../utils/url';
|
||||
import Link from '../Link';
|
||||
import Logo from '../Logo';
|
||||
import Label from '../Label';
|
||||
import CopyToClipBoard from '../CopyToClipBoard/index';
|
||||
import RegistryInfoDialog from '../RegistryInfoDialog';
|
||||
import AutoComplete from '../AutoComplete';
|
||||
import Label from '../Label';
|
||||
|
||||
import type { Node } from 'react';
|
||||
import { IProps, IState } from './interfaces';
|
||||
import { Wrapper, InnerWrapper, Greetings } from './styles';
|
||||
import { IProps, IState } from './types';
|
||||
import colors from '../../utils/styles/colors';
|
||||
import { Greetings, NavBar, InnerNavBar, MobileNavBar, InnerMobileNavBar, LeftSide, RightSide, Search, IconSearchButton } from './styles';
|
||||
|
||||
class Header extends Component<IProps, IState> {
|
||||
handleLoggedInMenu: Function;
|
||||
@ -32,20 +36,27 @@ class Header extends Component<IProps, IState> {
|
||||
handleToggleLogin: Function;
|
||||
renderInfoDialog: Function;
|
||||
|
||||
constructor(props: Object) {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
this.handleLoggedInMenu = this.handleLoggedInMenu.bind(this);
|
||||
this.handleLoggedInMenuClose = this.handleLoggedInMenuClose.bind(this);
|
||||
this.handleOpenRegistryInfoDialog = this.handleOpenRegistryInfoDialog.bind(this);
|
||||
this.handleCloseRegistryInfoDialog = this.handleCloseRegistryInfoDialog.bind(this);
|
||||
this.handleToggleLogin = this.handleToggleLogin.bind(this);
|
||||
this.renderInfoDialog = this.renderInfoDialog.bind(this);
|
||||
const { packages = [] } = props;
|
||||
this.state = {
|
||||
openInfoDialog: false,
|
||||
registryUrl: '',
|
||||
packages,
|
||||
showMobileNavBar: false,
|
||||
};
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps(nextProps: IProps, prevState: IState) {
|
||||
if (nextProps.packages !== prevState.packages) {
|
||||
return {
|
||||
packages: nextProps.packages,
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const registryUrl = getRegistryURL();
|
||||
this.setState({
|
||||
@ -56,65 +67,104 @@ class Header extends Component<IProps, IState> {
|
||||
/**
|
||||
* opens popover menu for logged in user.
|
||||
*/
|
||||
handleLoggedInMenu(event: SyntheticEvent<HTMLElement>) {
|
||||
handleLoggedInMenu = (event: SyntheticEvent<HTMLElement>) => {
|
||||
this.setState({
|
||||
anchorEl: event.currentTarget,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* closes popover menu for logged in user
|
||||
*/
|
||||
handleLoggedInMenuClose() {
|
||||
handleLoggedInMenuClose = () => {
|
||||
this.setState({
|
||||
anchorEl: null,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* opens registry information dialog.
|
||||
*/
|
||||
handleOpenRegistryInfoDialog() {
|
||||
handleOpenRegistryInfoDialog = () => {
|
||||
this.setState({
|
||||
openInfoDialog: true,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* closes registry information dialog.
|
||||
*/
|
||||
handleCloseRegistryInfoDialog() {
|
||||
handleCloseRegistryInfoDialog = () => {
|
||||
this.setState({
|
||||
openInfoDialog: false,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* close/open popover menu for logged in users.
|
||||
*/
|
||||
handleToggleLogin() {
|
||||
handleToggleLogin = () => {
|
||||
this.setState(
|
||||
{
|
||||
anchorEl: null,
|
||||
},
|
||||
this.props.toggleLoginModal
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
renderLeftSide(): Node {
|
||||
const { registryUrl } = this.state;
|
||||
handleToggleMNav = () => {
|
||||
this.setState({
|
||||
showMobileNavBar: !this.state.showMobileNavBar,
|
||||
});
|
||||
};
|
||||
|
||||
handleDismissMNav = () => {
|
||||
this.setState({
|
||||
showMobileNavBar: false,
|
||||
});
|
||||
};
|
||||
|
||||
renderLeftSide = (): Node => {
|
||||
const { packages } = this.state;
|
||||
const { onSearch = () => {}, search = '', withoutSearch = false, ...others } = this.props;
|
||||
return (
|
||||
<a href={`${registryUrl}/#/`}>
|
||||
<Logo />
|
||||
</a>
|
||||
<LeftSide>
|
||||
<Link to="/" style={{ marginRight: '1em' }}>
|
||||
<Logo />
|
||||
</Link>
|
||||
{!withoutSearch && (
|
||||
<Search>
|
||||
<AutoComplete
|
||||
suggestions={packages}
|
||||
onChange={onSearch}
|
||||
value={search}
|
||||
placeholder="Search packages"
|
||||
color={colors.white}
|
||||
startAdornment={
|
||||
<InputAdornment position="start" style={{ color: colors.white }}>
|
||||
<IconSearch />
|
||||
</InputAdornment>
|
||||
}
|
||||
{...others}
|
||||
/>
|
||||
</Search>
|
||||
)}
|
||||
</LeftSide>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
renderRightSide(): Node {
|
||||
const { username = '' } = this.props;
|
||||
renderRightSide = (): Node => {
|
||||
const { username = '', withoutSearch = false } = this.props;
|
||||
const installationLink = 'https://verdaccio.org/docs/en/installation';
|
||||
return (
|
||||
<div>
|
||||
<RightSide>
|
||||
{!withoutSearch && (
|
||||
<Tooltip title="Search packages" disableFocusListener>
|
||||
<IconSearchButton color="inherit" onClick={this.handleToggleMNav}>
|
||||
<IconSearch />
|
||||
</IconSearchButton>
|
||||
</Tooltip>
|
||||
)}
|
||||
<Tooltip title="Documentation" disableFocusListener>
|
||||
<IconButton color="inherit" component={Link} to={installationLink} blank>
|
||||
<Help />
|
||||
@ -132,20 +182,20 @@ class Header extends Component<IProps, IState> {
|
||||
Login
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</RightSide>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* render popover menu
|
||||
*/
|
||||
renderMenu(): Node {
|
||||
const { handleLogout, username = '' } = this.props;
|
||||
renderMenu = (): Node => {
|
||||
const { onLogout, username = '' } = this.props;
|
||||
const { anchorEl } = this.state;
|
||||
const open = Boolean(anchorEl);
|
||||
return (
|
||||
<React.Fragment>
|
||||
<IconButton id="header--button-account" aria-owns="sidebar-menu" aria-haspopup="true" color="inherit" onClick={this.handleLoggedInMenu}>
|
||||
<IconButton id="header--button-account" color="inherit" onClick={this.handleLoggedInMenu}>
|
||||
<AccountCircle />
|
||||
</IconButton>
|
||||
<Menu
|
||||
@ -166,15 +216,15 @@ class Header extends Component<IProps, IState> {
|
||||
<Greetings>{`Hi,`}</Greetings>
|
||||
<Label text={username} limit={140} weight="bold" capitalize />
|
||||
</MenuItem>
|
||||
<MenuItem onClick={handleLogout} id="header--button-logout">
|
||||
<MenuItem onClick={onLogout} id="header--button-logout">
|
||||
Logout
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
renderInfoDialog(): Node {
|
||||
renderInfoDialog = (): Node => {
|
||||
const { scope } = this.props;
|
||||
const { openInfoDialog, registryUrl } = this.state;
|
||||
return (
|
||||
@ -185,17 +235,32 @@ class Header extends Component<IProps, IState> {
|
||||
</div>
|
||||
</RegistryInfoDialog>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { packages, showMobileNavBar } = this.state;
|
||||
const { onSearch = () => {}, search = '', withoutSearch = false, ...others } = this.props;
|
||||
return (
|
||||
<Wrapper position="static">
|
||||
<InnerWrapper>
|
||||
{this.renderLeftSide()}
|
||||
{this.renderRightSide()}
|
||||
</InnerWrapper>
|
||||
{this.renderInfoDialog()}
|
||||
</Wrapper>
|
||||
<div>
|
||||
<NavBar position="static">
|
||||
<InnerNavBar>
|
||||
{this.renderLeftSide()}
|
||||
{this.renderRightSide()}
|
||||
</InnerNavBar>
|
||||
{this.renderInfoDialog()}
|
||||
</NavBar>
|
||||
{showMobileNavBar &&
|
||||
!withoutSearch && (
|
||||
<MobileNavBar>
|
||||
<InnerMobileNavBar>
|
||||
<AutoComplete suggestions={packages} onChange={onSearch} value={search} placeholder="Search packages" disableUnderline {...others} />
|
||||
</InnerMobileNavBar>
|
||||
<Button color="inherit" onClick={this.handleDismissMNav}>
|
||||
Cancel
|
||||
</Button>
|
||||
</MobileNavBar>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +0,0 @@
|
||||
/**
|
||||
* @prettier
|
||||
* @flow
|
||||
*/
|
||||
|
||||
export interface IProps {
|
||||
username?: string;
|
||||
handleLogout: Function;
|
||||
toggleLoginModal: Function;
|
||||
scope: string;
|
||||
}
|
||||
|
||||
export interface IState {
|
||||
anchorEl?: any;
|
||||
openInfoDialog: boolean;
|
||||
registryUrl: string;
|
||||
}
|
@ -1,38 +1,102 @@
|
||||
/**
|
||||
* @prettier
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import styled, { css } from 'react-emotion';
|
||||
import AppBar from '@material-ui/core/AppBar/index';
|
||||
import Toolbar from '@material-ui/core/Toolbar/index';
|
||||
import colors from '../../utils/styles/colors';
|
||||
import mq from '../../utils/styles/media';
|
||||
|
||||
export const Wrapper = styled(AppBar)`
|
||||
&& {
|
||||
background-color: ${colors.primary};
|
||||
position: fixed;
|
||||
}
|
||||
`;
|
||||
|
||||
export const InnerWrapper = styled(Toolbar)`
|
||||
&& {
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 20px;
|
||||
${mq.medium(css`
|
||||
min-width: 400px;
|
||||
max-width: 800px;
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
`)};
|
||||
${mq.large(css`
|
||||
max-width: 1240px;
|
||||
`)};
|
||||
}
|
||||
`;
|
||||
|
||||
export const Greetings = styled.span`
|
||||
margin: 0 5px 0 0;
|
||||
`;
|
||||
/**
|
||||
* @prettier
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import styled, { css } from 'react-emotion';
|
||||
import AppBar from '@material-ui/core/AppBar/index';
|
||||
import Toolbar from '@material-ui/core/Toolbar/index';
|
||||
import IconButton from '@material-ui/core/IconButton/index';
|
||||
|
||||
import colors from '../../utils/styles/colors';
|
||||
import mq from '../../utils/styles/media';
|
||||
|
||||
export const NavBar = styled(AppBar)`
|
||||
&& {
|
||||
background-color: ${colors.primary};
|
||||
min-height: 60px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
`;
|
||||
|
||||
export const InnerNavBar = styled(Toolbar)`
|
||||
&& {
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 20px;
|
||||
${mq.medium(css`
|
||||
min-width: 400px;
|
||||
max-width: 800px;
|
||||
width: 100%;
|
||||
margin: auto;
|
||||
`)};
|
||||
${mq.large(css`
|
||||
max-width: 1240px;
|
||||
`)};
|
||||
}
|
||||
`;
|
||||
|
||||
export const Greetings = styled.span`
|
||||
&& {
|
||||
margin: 0 5px 0 0;
|
||||
}
|
||||
`;
|
||||
|
||||
export const RightSide = styled(Toolbar)`
|
||||
&& {
|
||||
display: flex;
|
||||
padding: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
export const LeftSide = styled(RightSide)`
|
||||
&& {
|
||||
flex: 1;
|
||||
}
|
||||
`;
|
||||
|
||||
export const MobileNavBar = styled.div`
|
||||
&& {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
border-bottom: 1px solid ${colors.greyLight};
|
||||
padding: 8px;
|
||||
position: relative;
|
||||
${mq.medium(css`
|
||||
display: none;
|
||||
`)};
|
||||
}
|
||||
`;
|
||||
|
||||
export const InnerMobileNavBar = styled.div`
|
||||
&& {
|
||||
border-radius: 4px;
|
||||
background-color: ${colors.greyLight};
|
||||
color: ${colors.white};
|
||||
width: 100%;
|
||||
padding: 0px 5px;
|
||||
margin: 0 10px 0 0;
|
||||
}
|
||||
`;
|
||||
|
||||
export const Search = styled.div`
|
||||
&& {
|
||||
display: none;
|
||||
max-width: 393px;
|
||||
width: 100%;
|
||||
display: none;
|
||||
${mq.medium(css`
|
||||
display: flex;
|
||||
`)};
|
||||
}
|
||||
`;
|
||||
|
||||
export const IconSearchButton = styled(IconButton)`
|
||||
&& {
|
||||
display: block;
|
||||
${mq.medium(css`
|
||||
display: none;
|
||||
`)};
|
||||
}
|
||||
`;
|
||||
|
23
src/webui/components/Header/types.js
Normal file
23
src/webui/components/Header/types.js
Normal file
@ -0,0 +1,23 @@
|
||||
/**
|
||||
* @prettier
|
||||
* @flow
|
||||
*/
|
||||
|
||||
export interface IProps {
|
||||
username?: string;
|
||||
onLogout?: Function;
|
||||
toggleLoginModal: Function;
|
||||
scope: string;
|
||||
search?: string;
|
||||
packages?: any[];
|
||||
withoutSearch?: boolean;
|
||||
onSearch?: (event: SyntheticKeyboardEvent<HTMLInputElement>) => void;
|
||||
}
|
||||
|
||||
export interface IState {
|
||||
anchorEl?: any;
|
||||
openInfoDialog: boolean;
|
||||
registryUrl: string;
|
||||
packages: any[];
|
||||
showMobileNavBar: boolean;
|
||||
}
|
@ -9,7 +9,7 @@ import { fontWeight } from '../../utils/styles/sizes';
|
||||
import ellipsis from '../../utils/styles/ellipsis';
|
||||
|
||||
import type { Node } from 'react';
|
||||
import { IProps } from './interfaces';
|
||||
import { IProps } from './types';
|
||||
|
||||
const Wrapper = styled.span`
|
||||
font-weight: ${({ weight }) => fontWeight[weight]};
|
||||
|
29
src/webui/components/Layout/index.js
Normal file
29
src/webui/components/Layout/index.js
Normal file
@ -0,0 +1,29 @@
|
||||
/**
|
||||
* @prettier
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import styled, { css } from 'react-emotion';
|
||||
|
||||
export const Content = styled.div`
|
||||
&& {
|
||||
background-color: #fff;
|
||||
flex: 1;
|
||||
position: relative;
|
||||
}
|
||||
`;
|
||||
|
||||
export const Container = styled.div`
|
||||
&& {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
overflow: hidden;
|
||||
${({ isLoading }) =>
|
||||
isLoading &&
|
||||
css`
|
||||
${Content} {
|
||||
background-color: #f5f6f8;
|
||||
}
|
||||
`}
|
||||
`;
|
@ -5,7 +5,7 @@
|
||||
|
||||
import React from 'react';
|
||||
import type { Node } from 'react';
|
||||
import { IProps } from './interfaces';
|
||||
import { IProps } from './types';
|
||||
|
||||
const Link = ({ children, to = '#', blank = false, ...props }: IProps): Node => (
|
||||
<a href={to} target={blank ? '_blank' : '_self'} {...props}>
|
||||
|
23
src/webui/components/Loading/index.js
Normal file
23
src/webui/components/Loading/index.js
Normal file
@ -0,0 +1,23 @@
|
||||
/**
|
||||
* @prettier
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import type { Node } from 'react';
|
||||
|
||||
import Logo from '../Logo';
|
||||
import Spinner from '../Spinner';
|
||||
|
||||
import { Wrapper, Badge } from './styles';
|
||||
|
||||
const Loading = (): Node => (
|
||||
<Wrapper>
|
||||
<Badge>
|
||||
<Logo md />
|
||||
</Badge>
|
||||
<Spinner />
|
||||
</Wrapper>
|
||||
);
|
||||
|
||||
export default Loading;
|
24
src/webui/components/Loading/styles.js
Normal file
24
src/webui/components/Loading/styles.js
Normal file
@ -0,0 +1,24 @@
|
||||
/**
|
||||
* @prettier
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import styled from 'react-emotion';
|
||||
|
||||
export const Wrapper = styled.div`
|
||||
&& {
|
||||
transform: translate(-50%, -50%);
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
position: absolute;
|
||||
}
|
||||
`;
|
||||
|
||||
export const Badge = styled.div`
|
||||
&& {
|
||||
margin: 0 0 30px 0;
|
||||
border-radius: 25px;
|
||||
box-shadow: 0 10px 20px 0 rgba(69, 58, 100, 0.2);
|
||||
background: #f7f8f6;
|
||||
}
|
||||
`;
|
@ -103,7 +103,6 @@ export default class LoginModal extends Component {
|
||||
return type === 'error' && (
|
||||
<SnackbarContent
|
||||
className={classes.loginError}
|
||||
aria-describedby="client-snackbar"
|
||||
message={
|
||||
<div
|
||||
id="client-snackbar"
|
||||
@ -129,7 +128,6 @@ export default class LoginModal extends Component {
|
||||
open={visibility}
|
||||
id="login--form-container"
|
||||
maxWidth="xs"
|
||||
aria-labelledby="login-dialog"
|
||||
fullWidth
|
||||
>
|
||||
<DialogTitle>Login</DialogTitle>
|
||||
@ -137,7 +135,6 @@ export default class LoginModal extends Component {
|
||||
{this.renderLoginError(error)}
|
||||
<FormControl
|
||||
error={!username.value && !username.pristine}
|
||||
aria-describedby='username'
|
||||
required={username.required}
|
||||
fullWidth
|
||||
>
|
||||
@ -156,7 +153,6 @@ export default class LoginModal extends Component {
|
||||
</FormControl>
|
||||
<FormControl
|
||||
error={!password.value && !password.pristine}
|
||||
aria-describedby='password'
|
||||
required={password.required}
|
||||
style={{ marginTop: '8px' }}
|
||||
fullWidth
|
||||
|
@ -3,19 +3,27 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import styled from 'react-emotion';
|
||||
import styled, { css } from 'react-emotion';
|
||||
import logo from './img/logo.svg';
|
||||
|
||||
const Logo = styled.div`
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
box-sizing: border-box;
|
||||
background-position: center;
|
||||
background-size: contain;
|
||||
background-image: url(${logo});
|
||||
background-repeat: no-repeat;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
&& {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
box-sizing: border-box;
|
||||
background-position: center;
|
||||
background-size: contain;
|
||||
background-image: url(${logo});
|
||||
background-repeat: no-repeat;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
${props =>
|
||||
props.md &&
|
||||
css`
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
`};
|
||||
}
|
||||
`;
|
||||
|
||||
export default Logo;
|
||||
|
@ -1,5 +1,4 @@
|
||||
@import '../../styles/variables';
|
||||
@import '../../styles/mixins';
|
||||
|
||||
.notFound {
|
||||
width: 100%;
|
||||
@ -7,9 +6,6 @@ .notFound {
|
||||
line-height: $line-height-xl;
|
||||
border: none;
|
||||
outline: none;
|
||||
@include border-bottom-default($grey-light);
|
||||
|
||||
&:focus {
|
||||
@include border-bottom-default($grey);
|
||||
}
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import classes from './404.scss';
|
||||
|
||||
const NotFound = (props) => {
|
||||
return (
|
||||
<div className={classes.notFound}>
|
||||
<div className={`container content ${classes.notFound}`}>
|
||||
<h1>Error 404 - {props.pkg}</h1>
|
||||
<hr/>
|
||||
<p>
|
||||
|
@ -21,7 +21,6 @@ .package {
|
||||
|
||||
.tags {
|
||||
margin: 0 0.5em 0.5em 0;
|
||||
white-space: nowrap;
|
||||
font-size: $font-size-sm;
|
||||
:global {
|
||||
.el-tag {
|
||||
|
@ -1,6 +1,5 @@
|
||||
import React from 'react';
|
||||
import React, { Fragment } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
|
||||
import Package from '../Package';
|
||||
import Help from '../Help';
|
||||
@ -15,63 +14,49 @@ export default class PackageList extends React.Component {
|
||||
help: PropTypes.bool
|
||||
};
|
||||
|
||||
renderPackges = () => {
|
||||
const { packages } = this.props;
|
||||
return (
|
||||
packages.length > 0 ? (
|
||||
<Fragment>
|
||||
<h1 className={classes.listTitle}>Available Packages</h1>
|
||||
{this.renderList()}
|
||||
</Fragment>
|
||||
) : (
|
||||
<NoItems
|
||||
className="package-no-items"
|
||||
text={'No items were found with that query'}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
renderList = () => {
|
||||
const { packages } = this.props;
|
||||
return (
|
||||
<ul>
|
||||
{packages.map((pkg, i) => {
|
||||
const { label: name, version, description, time, keywords } = pkg;
|
||||
const author = formatAuthor(pkg.author);
|
||||
const license = formatLicense(pkg.license);
|
||||
return (
|
||||
<li key={i}>
|
||||
<Package {...{ name, version, author, description, license, time, keywords }} />
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { help } = this.props;
|
||||
return (
|
||||
<div className="package-list-items">
|
||||
<div className={classes.pkgContainer}>
|
||||
{this.renderTitle()}
|
||||
{this.isTherePackages() ? this.renderList() : this.renderOptions()}
|
||||
{help ? <Help /> : this.renderPackges()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderTitle() {
|
||||
if (this.isTherePackages() === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
return <h1 className={classes.listTitle}>Available Packages</h1>;
|
||||
}
|
||||
|
||||
renderList() {
|
||||
return this.props.packages.map((pkg, i) => {
|
||||
const {name, version, description, time, keywords} = pkg;
|
||||
const author = formatAuthor(pkg.author);
|
||||
const license = formatLicense(pkg.license);
|
||||
return (
|
||||
<li key={i}>
|
||||
<Package {...{name, version, author, description, license, time, keywords}} />
|
||||
</li>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
renderOptions() {
|
||||
if (this.isTherePackages() === false && this.props.help) {
|
||||
return this.renderHelp();
|
||||
} else {
|
||||
return this.renderNoItems();
|
||||
}
|
||||
}
|
||||
|
||||
renderNoItems() {
|
||||
return (
|
||||
<NoItems
|
||||
className="package-no-items"
|
||||
text={'No items were found with that query'}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
renderHelp() {
|
||||
if (this.props.help === false) {
|
||||
return;
|
||||
}
|
||||
return <Help />;
|
||||
}
|
||||
|
||||
isTherePackages() {
|
||||
return isEmpty(this.props.packages) === false;
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,6 @@ .pkgContainer {
|
||||
.listTitle {
|
||||
font-weight: $font-weight-regular;
|
||||
font-size: $font-size-xl;
|
||||
margin-top: 30px;
|
||||
margin-bottom: 0;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ import { Title, Content } from './styles';
|
||||
|
||||
import type { Node } from 'react';
|
||||
|
||||
import { IProps } from './interfaces';
|
||||
import { IProps } from './types';
|
||||
|
||||
const RegistryInfoDialog = ({ open = false, children, onClose }: IProps): Node => (
|
||||
<Dialog id="registryInfo--dialog-container" open={open} onClose={onClose}>
|
||||
|
@ -1,35 +0,0 @@
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import classes from './search.scss';
|
||||
|
||||
const noSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
const Search = (props) => {
|
||||
return (
|
||||
<form autoComplete="off" onSubmit={noSubmit}>
|
||||
<input
|
||||
name="search-box"
|
||||
type="text"
|
||||
placeholder={props.placeHolder}
|
||||
className={classes.searchBox}
|
||||
onChange={props.handleSearchInput}
|
||||
autoComplete="off"
|
||||
/>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
Search.defaultProps = {
|
||||
placeHolder: 'Type to search...'
|
||||
};
|
||||
|
||||
Search.propTypes = {
|
||||
handleSearchInput: PropTypes.func.isRequired,
|
||||
placeHolder: PropTypes.string,
|
||||
};
|
||||
|
||||
export default Search;
|
@ -1,5 +0,0 @@
|
||||
@import '../../styles/mixins';
|
||||
|
||||
.searchBox {
|
||||
@include searchBox;
|
||||
}
|
@ -6,10 +6,10 @@
|
||||
import React from 'react';
|
||||
import type { Node } from 'react';
|
||||
|
||||
import { IProps } from './interfaces';
|
||||
import { IProps } from './types';
|
||||
import { Wrapper, Circular } from './styles';
|
||||
|
||||
const Spinner = ({ size = 50, centered = true }: IProps): Node => (
|
||||
const Spinner = ({ size = 50, centered = false }: IProps): Node => (
|
||||
<Wrapper centered={centered}>
|
||||
<Circular size={size} />
|
||||
</Wrapper>
|
||||
|
@ -1,15 +1,25 @@
|
||||
import styled from 'react-emotion';
|
||||
import CircularProgress from '@material-ui/core/CircularProgress';
|
||||
/**
|
||||
* @prettier
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import styled, { css } from 'react-emotion';
|
||||
import CircularProgress from '@material-ui/core/CircularProgress/index';
|
||||
import colors from '../../utils/styles/colors';
|
||||
|
||||
export const Wrapper = styled.div`
|
||||
&& {
|
||||
${({ centered }) => centered && `
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
`}
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
${props =>
|
||||
props.centered &&
|
||||
css`
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
`}
|
||||
`;
|
||||
|
||||
export const Circular = styled(CircularProgress)`
|
||||
|
19
src/webui/components/TxtField/index.js
Normal file
19
src/webui/components/TxtField/index.js
Normal file
@ -0,0 +1,19 @@
|
||||
/**
|
||||
* @prettier
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import TextField, { TextFieldProps } from '@material-ui/core/TextField';
|
||||
|
||||
const TxtField = ({ InputProps, classes, ...other }: TextFieldProps) => (
|
||||
<TextField
|
||||
{...other}
|
||||
InputProps={{
|
||||
...InputProps,
|
||||
classes,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
export default TxtField;
|
@ -1,193 +0,0 @@
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import Dialog from '@material-ui/core/Dialog';
|
||||
import DialogActions from '@material-ui/core/DialogActions';
|
||||
import DialogContent from '@material-ui/core/DialogContent';
|
||||
import DialogTitle from '@material-ui/core/DialogTitle';
|
||||
import SnackbarContent from '@material-ui/core/SnackbarContent';
|
||||
import ErrorIcon from '@material-ui/icons/Error';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
import debounce from 'lodash/debounce';
|
||||
|
||||
import API from '../../utils/api';
|
||||
|
||||
import PackageList from '../../components/PackageList';
|
||||
import Search from '../../components/Search';
|
||||
import Spinner from '../../components/Spinner';
|
||||
|
||||
import classes from "./home.scss";
|
||||
|
||||
class Home extends Component {
|
||||
static propTypes = {
|
||||
children: PropTypes.element,
|
||||
isUserLoggedIn: PropTypes.bool
|
||||
};
|
||||
|
||||
state = {
|
||||
showAlertDialog: false,
|
||||
alertDialogContent: {
|
||||
title: '',
|
||||
message: ''
|
||||
},
|
||||
loading: true,
|
||||
fistTime: true,
|
||||
query: ''
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.handleSearchInput = this.handleSearchInput.bind(this);
|
||||
this.handleShowAlertDialog = this.handleShowAlertDialog.bind(this);
|
||||
this.handleCloseAlertDialog = this.handleCloseAlertDialog.bind(this);
|
||||
this.searchPackage = debounce(this.searchPackage, 800);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.loadPackages();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
if (prevState.query !== this.state.query) {
|
||||
if (this.req && this.req.abort) this.req.abort();
|
||||
this.setState({
|
||||
loading: true
|
||||
});
|
||||
|
||||
if (prevState.query !== '' && this.state.query === '') {
|
||||
this.loadPackages();
|
||||
} else {
|
||||
this.searchPackage(this.state.query);
|
||||
}
|
||||
}
|
||||
|
||||
if (prevProps.isUserLoggedIn !== this.props.isUserLoggedIn) {
|
||||
this.loadPackages();
|
||||
}
|
||||
}
|
||||
|
||||
async loadPackages() {
|
||||
try {
|
||||
this.req = await API.request('packages', 'GET');
|
||||
|
||||
if (this.state.query === '') {
|
||||
this.setState({
|
||||
packages: this.req,
|
||||
loading: false
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
this.handleShowAlertDialog({
|
||||
title: 'Warning',
|
||||
message: `Unable to load package list: ${error.error}`
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async searchPackage(query) {
|
||||
try {
|
||||
this.req = await API.request(`/search/${query}`, 'GET');
|
||||
|
||||
// Implement cancel feature later
|
||||
if (this.state.query === query) {
|
||||
this.setState({
|
||||
packages: this.req,
|
||||
fistTime: false,
|
||||
loading: false
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
this.handleShowAlertDialog({
|
||||
title: 'Warning',
|
||||
message: 'Unable to get search result, please try again later.'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderAlertDialog() {
|
||||
return (
|
||||
<Dialog
|
||||
open={this.state.showAlertDialog}
|
||||
onClose={this.handleCloseAlertDialog}
|
||||
aria-labelledby="alert-dialog-title"
|
||||
>
|
||||
<DialogTitle id="alert-dialog-title">
|
||||
{this.state.alertDialogContent.title}
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<SnackbarContent
|
||||
className={classes.alertError}
|
||||
aria-describedby="client-snackbar"
|
||||
message={
|
||||
<div
|
||||
id="client-snackbar"
|
||||
className={classes.alertErrorMsg}
|
||||
>
|
||||
<ErrorIcon className={classes.alertIcon} />
|
||||
<span>
|
||||
{this.state.alertDialogContent.message}
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button
|
||||
onClick={this.handleCloseAlertDialog}
|
||||
color="primary"
|
||||
autoFocus
|
||||
>
|
||||
Ok
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
handleShowAlertDialog(content) {
|
||||
this.setState({
|
||||
showAlertDialog: true,
|
||||
alertDialogContent: content
|
||||
});
|
||||
};
|
||||
|
||||
handleCloseAlertDialog() {
|
||||
this.setState({
|
||||
showAlertDialog: false
|
||||
});
|
||||
};
|
||||
|
||||
handleSearchInput(e) {
|
||||
this.setState({
|
||||
query: e.target.value.trim()
|
||||
});
|
||||
}
|
||||
|
||||
isTherePackages() {
|
||||
return isEmpty(this.state.packages);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { packages, loading } = this.state;
|
||||
return (
|
||||
<Fragment>
|
||||
{this.renderSearchBar()}
|
||||
{loading ? (
|
||||
<Spinner centered />
|
||||
) : (
|
||||
<PackageList help={isEmpty(packages) === true} packages={packages} />
|
||||
)}
|
||||
{this.renderAlertDialog()}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
renderSearchBar() {
|
||||
if (this.isTherePackages() && this.state.fistTime) {
|
||||
return;
|
||||
}
|
||||
return <Search handleSearchInput={this.handleSearchInput} />;
|
||||
}
|
||||
}
|
||||
|
||||
export default Home;
|
@ -3,7 +3,6 @@
|
||||
|
||||
.twoColumn {
|
||||
@include container-size;
|
||||
margin: 0 10px;
|
||||
display: flex;
|
||||
|
||||
> div {
|
@ -67,12 +67,14 @@ export default class Detail extends Component {
|
||||
const { notFound, readMe } = this.state;
|
||||
|
||||
if (notFound) {
|
||||
return <NotFound pkg={this.packageName} />;
|
||||
return (
|
||||
<NotFound pkg={this.packageName} />
|
||||
);
|
||||
} else if (isEmpty(readMe)) {
|
||||
return <Spinner centered />;
|
||||
}
|
||||
return (
|
||||
<div className={classes.twoColumn}>
|
||||
<div className={`container content ${classes.twoColumn}`}>
|
||||
<PackageDetail readMe={readMe} packageName={this.packageName} />
|
||||
<PackageSidebar packageName={this.packageName} />
|
||||
</div>
|
47
src/webui/pages/home/index.js
Normal file
47
src/webui/pages/home/index.js
Normal file
@ -0,0 +1,47 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import PackageList from '../../components/PackageList';
|
||||
|
||||
class Home extends Component {
|
||||
static propTypes = {
|
||||
children: PropTypes.element,
|
||||
isUserLoggedIn: PropTypes.bool,
|
||||
packages: PropTypes.array,
|
||||
filteredPackages: PropTypes.array,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
fistTime: true,
|
||||
packages: props.packages,
|
||||
filteredPackages: props.filteredPackages
|
||||
};
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps(nextProps, prevState) {
|
||||
if (nextProps.packages !== prevState.packages) {
|
||||
return {
|
||||
packages: nextProps.packages,
|
||||
};
|
||||
}
|
||||
if (nextProps.filteredPackages !== prevState.filteredPackages) {
|
||||
return {
|
||||
filteredPackages: nextProps.filteredPackages,
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { filteredPackages, packages } = this.state;
|
||||
return (
|
||||
<div className="container content">
|
||||
<PackageList help={!packages.length > 0} packages={filteredPackages} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Home;
|
@ -4,40 +4,40 @@ import {HashRouter as Router, Route, Switch} from 'react-router-dom';
|
||||
|
||||
import {asyncComponent} from './utils/asyncComponent';
|
||||
|
||||
const DetailPackage = asyncComponent(() => import('./modules/detail'));
|
||||
const HomePage = asyncComponent(() => import('./modules/home'));
|
||||
const DetailPackage = asyncComponent(() => import('./pages/detail'));
|
||||
import HomePage from './pages/home';
|
||||
|
||||
class RouterApp extends Component {
|
||||
static propTypes = {
|
||||
isUserLoggedIn: PropTypes.bool
|
||||
};
|
||||
|
||||
render() {
|
||||
const {isUserLoggedIn} = this.props;
|
||||
return (
|
||||
<Router>
|
||||
<div className="container content">
|
||||
<Switch>
|
||||
<Route
|
||||
exact
|
||||
path="/(search/:keyword)?"
|
||||
render={() => <HomePage isUserLoggedIn={isUserLoggedIn} />}
|
||||
path="/"
|
||||
render={() => (
|
||||
<HomePage {...this.props} />
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path="/detail/@:scope/:package"
|
||||
render={(props) => (
|
||||
<DetailPackage {...props} isUserLoggedIn={isUserLoggedIn} />
|
||||
<DetailPackage {...props} {...this.props} />
|
||||
)}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path="/detail/:package"
|
||||
render={(props) => (
|
||||
<DetailPackage {...props} isUserLoggedIn={isUserLoggedIn} />
|
||||
<DetailPackage {...props} {...this.props} />
|
||||
)}
|
||||
/>
|
||||
</Switch>
|
||||
</div>
|
||||
</Router>
|
||||
);
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
:global {
|
||||
.container {
|
||||
margin-top: 94px;
|
||||
margin-top: 20px;
|
||||
flex: 1;
|
||||
|
||||
@include container-size;
|
||||
@ -15,13 +15,13 @@ :global {
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.page-full-height {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.el-button {
|
||||
@ -40,4 +40,8 @@ :global {
|
||||
.el-dialog__headerbtn:hover .el-dialog__close {
|
||||
color: $eclipse;
|
||||
}
|
||||
|
||||
.package-list-items {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
@ -16,8 +16,8 @@ export const lineHeight = {
|
||||
};
|
||||
|
||||
export const fontWeight = {
|
||||
light: 400,
|
||||
light: 300,
|
||||
regular: 400,
|
||||
semiBold: 600,
|
||||
semiBold: 500,
|
||||
bold: 700
|
||||
};
|
||||
|
@ -27,8 +27,6 @@ jest.mock('../../../src/webui/utils/storage', () => {
|
||||
return new LocalStorageMock();
|
||||
});
|
||||
|
||||
jest.mock('element-theme-default', () => ({}));
|
||||
|
||||
jest.mock('../../../src/webui/utils/api', () => ({
|
||||
request: require('./components/__mocks__/api').default.request
|
||||
}));
|
||||
@ -66,15 +64,14 @@ describe('App', () => {
|
||||
expect(wrapper.state('user').username).toEqual('verdaccio');
|
||||
});
|
||||
|
||||
it('handleLogout - logouts the user and clear localstorage', () => {
|
||||
it('handleLogout - logouts the user and clear localstorage', async () => {
|
||||
const { handleLogout } = wrapper.instance();
|
||||
storage.setItem('username', 'verdaccio');
|
||||
storage.setItem('token', 'xxxx.TOKEN.xxxx');
|
||||
|
||||
handleLogout();
|
||||
expect(handleLogout()).toBeUndefined();
|
||||
await handleLogout();
|
||||
expect(wrapper.state('user')).toEqual({});
|
||||
expect(wrapper.state('isLoggedIn')).toBeFalsy();
|
||||
expect(wrapper.state('isUserLoggedIn')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('doLogin - login the user successfully', async () => {
|
||||
@ -84,11 +81,11 @@ describe('App', () => {
|
||||
username: 'sam',
|
||||
token: 'TEST_TOKEN'
|
||||
};
|
||||
expect(wrapper.state('user')).toEqual(result);
|
||||
expect(wrapper.state('isUserLoggedIn')).toBeTruthy();
|
||||
expect(wrapper.state('showLoginModal')).toBeFalsy();
|
||||
expect(storage.getItem('username')).toEqual('sam');
|
||||
expect(storage.getItem('token')).toEqual('TEST_TOKEN');
|
||||
expect(wrapper.state('user')).toEqual(result);
|
||||
});
|
||||
|
||||
it('doLogin - authentication failure', async () => {
|
||||
|
@ -1,3 +1,3 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<CopyToClipBoard /> component render the component 1`] = `"<p class=\\"css-1cxz858 eduq2bv0\\"><span class=\\"css-1m8aenu eduq2bv1\\">copy text</span><button tabindex=\\"0\\" class=\\"MuiButtonBase-root-14 MuiIconButton-root-8 css-56v3u0 eduq2bv2\\" type=\\"button\\" aria-label=\\"Copy to Clipboard\\" title=\\"Copy to Clipboard\\"><span class=\\"MuiIconButton-label-13\\"><svg class=\\"MuiSvgIcon-root-17\\" focusable=\\"false\\" viewBox=\\"0 0 24 24\\" aria-hidden=\\"true\\" role=\\"presentation\\"><path fill=\\"none\\" d=\\"M0 0h24v24H0z\\"></path><path d=\\"M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm-1 4l6 6v10c0 1.1-.9 2-2 2H7.99C6.89 23 6 22.1 6 21l.01-14c0-1.1.89-2 1.99-2h7zm-1 7h5.5L14 6.5V12z\\"></path></svg></span><span class=\\"MuiTouchRipple-root-26\\"></span></button></p>"`;
|
||||
exports[`<CopyToClipBoard /> component render the component 1`] = `"<p class=\\"css-1cxz858 eduq2bv0\\"><span class=\\"css-1m8aenu eduq2bv1\\">copy text</span><button tabindex=\\"0\\" class=\\"MuiButtonBase-root-14 MuiIconButton-root-8 css-56v3u0 eduq2bv2\\" type=\\"button\\" title=\\"Copy to Clipboard\\"><span class=\\"MuiIconButton-label-13\\"><svg class=\\"MuiSvgIcon-root-17\\" focusable=\\"false\\" viewBox=\\"0 0 24 24\\" aria-hidden=\\"true\\" role=\\"presentation\\"><path fill=\\"none\\" d=\\"M0 0h24v24H0z\\"></path><path d=\\"M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm-1 4l6 6v10c0 1.1-.9 2-2 2H7.99C6.89 23 6 22.1 6 21l.01-14c0-1.1.89-2 1.99-2h7zm-1 7h5.5L14 6.5V12z\\"></path></svg></span><span class=\\"MuiTouchRipple-root-26\\"></span></button></p>"`;
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<Header /> component with logged in state should load the component in logged in state 1`] = `"<header class=\\"MuiPaper-root-10 MuiPaper-elevation4-16 MuiAppBar-root-1 MuiAppBar-positionStatic-5 MuiAppBar-colorPrimary-8 css-6njnwn e1ctrp120\\"><div class=\\"MuiToolbar-root-37 MuiToolbar-regular-39 MuiToolbar-gutters-38 css-ioebmw e1ctrp121\\"><a href=\\"http://localhost/#/\\"><div class=\\"css-guljh6 e18wxr160\\"></div></a><div><a href=\\"https://verdaccio.org/docs/en/installation\\" target=\\"_blank\\" tabindex=\\"0\\" class=\\"MuiButtonBase-root-54 MuiIconButton-root-48 MuiIconButton-colorInherit-49\\" role=\\"button\\" title=\\"Documentation\\"><span class=\\"MuiIconButton-label-53\\"><svg class=\\"MuiSvgIcon-root-57\\" focusable=\\"false\\" viewBox=\\"0 0 24 24\\" aria-hidden=\\"true\\" role=\\"presentation\\"><path fill=\\"none\\" d=\\"M0 0h24v24H0z\\"></path><path d=\\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92C13.45 12.9 13 13.5 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41 0-1.1-.9-2-2-2s-2 .9-2 2H8c0-2.21 1.79-4 4-4s4 1.79 4 4c0 .88-.36 1.68-.93 2.25z\\"></path></svg></span><span class=\\"MuiTouchRipple-root-66\\"></span></a><button tabindex=\\"0\\" class=\\"MuiButtonBase-root-54 MuiIconButton-root-48 MuiIconButton-colorInherit-49\\" type=\\"button\\" id=\\"header--button-registryInfo\\" title=\\"Registry Information\\"><span class=\\"MuiIconButton-label-53\\"><svg class=\\"MuiSvgIcon-root-57\\" focusable=\\"false\\" viewBox=\\"0 0 24 24\\" aria-hidden=\\"true\\" role=\\"presentation\\"><path fill=\\"none\\" d=\\"M0 0h24v24H0z\\"></path><path d=\\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z\\"></path></svg></span><span class=\\"MuiTouchRipple-root-66\\"></span></button><button tabindex=\\"0\\" class=\\"MuiButtonBase-root-54 MuiIconButton-root-48 MuiIconButton-colorInherit-49\\" type=\\"button\\" id=\\"header--button-account\\" aria-owns=\\"sidebar-menu\\" aria-haspopup=\\"true\\"><span class=\\"MuiIconButton-label-53\\"><svg class=\\"MuiSvgIcon-root-57\\" focusable=\\"false\\" viewBox=\\"0 0 24 24\\" aria-hidden=\\"true\\" role=\\"presentation\\"><path d=\\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 3c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm0 14.2c-2.5 0-4.71-1.28-6-3.22.03-1.99 4-3.08 6-3.08 1.99 0 5.97 1.09 6 3.08-1.29 1.94-3.5 3.22-6 3.22z\\"></path><path fill=\\"none\\" d=\\"M0 0h24v24H0z\\"></path></svg></span><span class=\\"MuiTouchRipple-root-66\\"></span></button></div></div></header>"`;
|
||||
exports[`<Header /> component with logged in state should load the component in logged in state 1`] = `"<div><header class=\\"MuiPaper-root-10 MuiPaper-elevation4-16 MuiAppBar-root-1 MuiAppBar-positionStatic-5 MuiAppBar-colorPrimary-8 css-1rvhilz e1ctrp120\\"><div class=\\"MuiToolbar-root-37 MuiToolbar-regular-39 MuiToolbar-gutters-38 css-ioebmw e1ctrp121\\"><div class=\\"MuiToolbar-root-37 MuiToolbar-regular-39 MuiToolbar-gutters-38 css-1vacr9s e1ctrp124\\"><a href=\\"/\\" target=\\"_self\\" style=\\"margin-right: 1em;\\"><div class=\\"css-12nq0oo e18wxr160\\"></div></a></div><div class=\\"MuiToolbar-root-37 MuiToolbar-regular-39 MuiToolbar-gutters-38 css-m61s5i e1ctrp123\\"><a href=\\"https://verdaccio.org/docs/en/installation\\" target=\\"_blank\\" tabindex=\\"0\\" class=\\"MuiButtonBase-root-54 MuiIconButton-root-48 MuiIconButton-colorInherit-49\\" role=\\"button\\" title=\\"Documentation\\"><span class=\\"MuiIconButton-label-53\\"><svg class=\\"MuiSvgIcon-root-57\\" focusable=\\"false\\" viewBox=\\"0 0 24 24\\" aria-hidden=\\"true\\" role=\\"presentation\\"><path fill=\\"none\\" d=\\"M0 0h24v24H0z\\"></path><path d=\\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92C13.45 12.9 13 13.5 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41 0-1.1-.9-2-2-2s-2 .9-2 2H8c0-2.21 1.79-4 4-4s4 1.79 4 4c0 .88-.36 1.68-.93 2.25z\\"></path></svg></span><span class=\\"MuiTouchRipple-root-66\\"></span></a><button tabindex=\\"0\\" class=\\"MuiButtonBase-root-54 MuiIconButton-root-48 MuiIconButton-colorInherit-49\\" type=\\"button\\" id=\\"header--button-registryInfo\\" title=\\"Registry Information\\"><span class=\\"MuiIconButton-label-53\\"><svg class=\\"MuiSvgIcon-root-57\\" focusable=\\"false\\" viewBox=\\"0 0 24 24\\" aria-hidden=\\"true\\" role=\\"presentation\\"><path fill=\\"none\\" d=\\"M0 0h24v24H0z\\"></path><path d=\\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z\\"></path></svg></span><span class=\\"MuiTouchRipple-root-66\\"></span></button><button tabindex=\\"0\\" class=\\"MuiButtonBase-root-54 MuiIconButton-root-48 MuiIconButton-colorInherit-49\\" type=\\"button\\" id=\\"header--button-account\\"><span class=\\"MuiIconButton-label-53\\"><svg class=\\"MuiSvgIcon-root-57\\" focusable=\\"false\\" viewBox=\\"0 0 24 24\\" aria-hidden=\\"true\\" role=\\"presentation\\"><path d=\\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 3c1.66 0 3 1.34 3 3s-1.34 3-3 3-3-1.34-3-3 1.34-3 3-3zm0 14.2c-2.5 0-4.71-1.28-6-3.22.03-1.99 4-3.08 6-3.08 1.99 0 5.97 1.09 6 3.08-1.29 1.94-3.5 3.22-6 3.22z\\"></path><path fill=\\"none\\" d=\\"M0 0h24v24H0z\\"></path></svg></span><span class=\\"MuiTouchRipple-root-66\\"></span></button></div></div></header></div>"`;
|
||||
|
||||
exports[`<Header /> component with logged out state should load the component in logged out state 1`] = `"<header class=\\"MuiPaper-root-10 MuiPaper-elevation4-16 MuiAppBar-root-1 MuiAppBar-positionStatic-5 MuiAppBar-colorPrimary-8 css-6njnwn e1ctrp120\\"><div class=\\"MuiToolbar-root-37 MuiToolbar-regular-39 MuiToolbar-gutters-38 css-ioebmw e1ctrp121\\"><a href=\\"http://localhost/#/\\"><div class=\\"css-guljh6 e18wxr160\\"></div></a><div><a href=\\"https://verdaccio.org/docs/en/installation\\" target=\\"_blank\\" tabindex=\\"0\\" class=\\"MuiButtonBase-root-54 MuiIconButton-root-48 MuiIconButton-colorInherit-49\\" role=\\"button\\" title=\\"Documentation\\"><span class=\\"MuiIconButton-label-53\\"><svg class=\\"MuiSvgIcon-root-57\\" focusable=\\"false\\" viewBox=\\"0 0 24 24\\" aria-hidden=\\"true\\" role=\\"presentation\\"><path fill=\\"none\\" d=\\"M0 0h24v24H0z\\"></path><path d=\\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92C13.45 12.9 13 13.5 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41 0-1.1-.9-2-2-2s-2 .9-2 2H8c0-2.21 1.79-4 4-4s4 1.79 4 4c0 .88-.36 1.68-.93 2.25z\\"></path></svg></span><span class=\\"MuiTouchRipple-root-66\\"></span></a><button tabindex=\\"0\\" class=\\"MuiButtonBase-root-54 MuiIconButton-root-48 MuiIconButton-colorInherit-49\\" type=\\"button\\" id=\\"header--button-registryInfo\\" title=\\"Registry Information\\"><span class=\\"MuiIconButton-label-53\\"><svg class=\\"MuiSvgIcon-root-57\\" focusable=\\"false\\" viewBox=\\"0 0 24 24\\" aria-hidden=\\"true\\" role=\\"presentation\\"><path fill=\\"none\\" d=\\"M0 0h24v24H0z\\"></path><path d=\\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z\\"></path></svg></span><span class=\\"MuiTouchRipple-root-66\\"></span></button><button tabindex=\\"0\\" class=\\"MuiButtonBase-root-54 MuiButton-root-108 MuiButton-text-110 MuiButton-flat-113 MuiButton-colorInherit-129\\" type=\\"button\\" id=\\"header--button-login\\"><span class=\\"MuiButton-label-109\\">Login</span><span class=\\"MuiTouchRipple-root-66\\"></span></button></div></div></header>"`;
|
||||
exports[`<Header /> component with logged out state should load the component in logged out state 1`] = `"<div><header class=\\"MuiPaper-root-10 MuiPaper-elevation4-16 MuiAppBar-root-1 MuiAppBar-positionStatic-5 MuiAppBar-colorPrimary-8 css-1rvhilz e1ctrp120\\"><div class=\\"MuiToolbar-root-37 MuiToolbar-regular-39 MuiToolbar-gutters-38 css-ioebmw e1ctrp121\\"><div class=\\"MuiToolbar-root-37 MuiToolbar-regular-39 MuiToolbar-gutters-38 css-1vacr9s e1ctrp124\\"><a href=\\"/\\" target=\\"_self\\" style=\\"margin-right: 1em;\\"><div class=\\"css-12nq0oo e18wxr160\\"></div></a></div><div class=\\"MuiToolbar-root-37 MuiToolbar-regular-39 MuiToolbar-gutters-38 css-m61s5i e1ctrp123\\"><a href=\\"https://verdaccio.org/docs/en/installation\\" target=\\"_blank\\" tabindex=\\"0\\" class=\\"MuiButtonBase-root-54 MuiIconButton-root-48 MuiIconButton-colorInherit-49\\" role=\\"button\\" title=\\"Documentation\\"><span class=\\"MuiIconButton-label-53\\"><svg class=\\"MuiSvgIcon-root-57\\" focusable=\\"false\\" viewBox=\\"0 0 24 24\\" aria-hidden=\\"true\\" role=\\"presentation\\"><path fill=\\"none\\" d=\\"M0 0h24v24H0z\\"></path><path d=\\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 17h-2v-2h2v2zm2.07-7.75l-.9.92C13.45 12.9 13 13.5 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41 0-1.1-.9-2-2-2s-2 .9-2 2H8c0-2.21 1.79-4 4-4s4 1.79 4 4c0 .88-.36 1.68-.93 2.25z\\"></path></svg></span><span class=\\"MuiTouchRipple-root-66\\"></span></a><button tabindex=\\"0\\" class=\\"MuiButtonBase-root-54 MuiIconButton-root-48 MuiIconButton-colorInherit-49\\" type=\\"button\\" id=\\"header--button-registryInfo\\" title=\\"Registry Information\\"><span class=\\"MuiIconButton-label-53\\"><svg class=\\"MuiSvgIcon-root-57\\" focusable=\\"false\\" viewBox=\\"0 0 24 24\\" aria-hidden=\\"true\\" role=\\"presentation\\"><path fill=\\"none\\" d=\\"M0 0h24v24H0z\\"></path><path d=\\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z\\"></path></svg></span><span class=\\"MuiTouchRipple-root-66\\"></span></button><button tabindex=\\"0\\" class=\\"MuiButtonBase-root-54 MuiButton-root-108 MuiButton-text-110 MuiButton-flat-113 MuiButton-colorInherit-129\\" type=\\"button\\" id=\\"header--button-login\\"><span class=\\"MuiButton-label-109\\">Login</span><span class=\\"MuiTouchRipple-root-66\\"></span></button></div></div></header></div>"`;
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<LoginModal /> should load the component in default state 1`] = `"<div class=\\"MuiModal-root-13 MuiDialog-root-1 MuiDialog-scrollPaper-2\\" role=\\"dialog\\" id=\\"login--form-container\\" aria-labelledby=\\"login-dialog\\"><div class=\\"MuiBackdrop-root-15\\" aria-hidden=\\"true\\" style=\\"opacity: 1; -webkit-transition: opacity 225ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;\\"></div><div class=\\"MuiPaper-root-17 MuiPaper-elevation24-43 MuiPaper-rounded-18 MuiDialog-paper-4 MuiDialog-paperScrollPaper-5 MuiDialog-paperWidthXs-7 MuiDialog-paperFullWidth-11\\" style=\\"opacity: 1; -webkit-transition: opacity 225ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;\\" role=\\"document\\" tabindex=\\"-1\\"><div class=\\"MuiDialogTitle-root-44\\"><h2 class=\\"MuiTypography-root-45 MuiTypography-title-51\\">Login</h2></div><div class=\\"MuiDialogContent-root-71\\"><div class=\\"MuiFormControl-root-72 MuiFormControl-fullWidth-75\\" aria-describedby=\\"username\\"><label class=\\"MuiFormLabel-root-83 MuiFormLabel-required-88 MuiInputLabel-root-76 MuiInputLabel-formControl-77 MuiInputLabel-animated-80\\" data-shrink=\\"false\\" for=\\"username\\">Username<span class=\\"MuiFormLabel-asterisk-89\\"> *</span></label><div class=\\"MuiInputBase-root-103 MuiInput-root-90 MuiInput-underline-94 MuiInputBase-formControl-104 MuiInput-formControl-91\\"><input aria-invalid=\\"false\\" class=\\"MuiInputBase-input-113 MuiInput-input-98\\" id=\\"login--form-username\\" placeholder=\\"Your username\\" required=\\"\\" type=\\"text\\" value=\\"\\"></div></div><div class=\\"MuiFormControl-root-72 MuiFormControl-fullWidth-75\\" aria-describedby=\\"password\\" style=\\"margin-top: 8px;\\"><label class=\\"MuiFormLabel-root-83 MuiFormLabel-required-88 MuiInputLabel-root-76 MuiInputLabel-formControl-77 MuiInputLabel-animated-80\\" data-shrink=\\"false\\" for=\\"password\\">Password<span class=\\"MuiFormLabel-asterisk-89\\"> *</span></label><div class=\\"MuiInputBase-root-103 MuiInput-root-90 MuiInput-underline-94 MuiInputBase-formControl-104 MuiInput-formControl-91\\"><input aria-invalid=\\"false\\" class=\\"MuiInputBase-input-113 MuiInput-input-98 MuiInputBase-inputType-116 MuiInput-inputType-101\\" id=\\"login--form-password\\" placeholder=\\"Your strong password\\" required=\\"\\" type=\\"password\\" value=\\"\\"></div></div></div><div class=\\"MuiDialogActions-root-120 dialog-footer\\"><button tabindex=\\"0\\" class=\\"MuiButtonBase-root-148 MuiButton-root-122 MuiButton-text-124 MuiButton-flat-127 MuiButton-colorInherit-143 MuiDialogActions-action-121\\" type=\\"button\\" id=\\"login--form-cancel\\"><span class=\\"MuiButton-label-123\\">Cancel</span><span class=\\"MuiTouchRipple-root-151\\"></span></button><button tabindex=\\"0\\" class=\\"MuiButtonBase-root-148 MuiButton-root-122 MuiButton-text-124 MuiButton-flat-127 MuiButton-colorInherit-143 MuiDialogActions-action-121\\" type=\\"button\\" id=\\"login--form-submit\\"><span class=\\"MuiButton-label-123\\">Login</span><span class=\\"MuiTouchRipple-root-151\\"></span></button></div></div></div>"`;
|
||||
exports[`<LoginModal /> should load the component in default state 1`] = `"<div class=\\"MuiModal-root-13 MuiDialog-root-1 MuiDialog-scrollPaper-2\\" role=\\"dialog\\" id=\\"login--form-container\\"><div class=\\"MuiBackdrop-root-15\\" aria-hidden=\\"true\\" style=\\"opacity: 1; -webkit-transition: opacity 225ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;\\"></div><div class=\\"MuiPaper-root-17 MuiPaper-elevation24-43 MuiPaper-rounded-18 MuiDialog-paper-4 MuiDialog-paperScrollPaper-5 MuiDialog-paperWidthXs-7 MuiDialog-paperFullWidth-11\\" style=\\"opacity: 1; -webkit-transition: opacity 225ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;\\" role=\\"document\\" tabindex=\\"-1\\"><div class=\\"MuiDialogTitle-root-44\\"><h2 class=\\"MuiTypography-root-45 MuiTypography-title-51\\">Login</h2></div><div class=\\"MuiDialogContent-root-71\\"><div class=\\"MuiFormControl-root-72 MuiFormControl-fullWidth-75\\"><label class=\\"MuiFormLabel-root-83 MuiFormLabel-required-88 MuiInputLabel-root-76 MuiInputLabel-formControl-77 MuiInputLabel-animated-80\\" data-shrink=\\"false\\" for=\\"username\\">Username<span class=\\"MuiFormLabel-asterisk-89\\"> *</span></label><div class=\\"MuiInputBase-root-103 MuiInput-root-90 MuiInput-underline-94 MuiInputBase-formControl-104 MuiInput-formControl-91\\"><input aria-invalid=\\"false\\" class=\\"MuiInputBase-input-113 MuiInput-input-98\\" id=\\"login--form-username\\" placeholder=\\"Your username\\" required=\\"\\" type=\\"text\\" value=\\"\\"></div></div><div class=\\"MuiFormControl-root-72 MuiFormControl-fullWidth-75\\" style=\\"margin-top: 8px;\\"><label class=\\"MuiFormLabel-root-83 MuiFormLabel-required-88 MuiInputLabel-root-76 MuiInputLabel-formControl-77 MuiInputLabel-animated-80\\" data-shrink=\\"false\\" for=\\"password\\">Password<span class=\\"MuiFormLabel-asterisk-89\\"> *</span></label><div class=\\"MuiInputBase-root-103 MuiInput-root-90 MuiInput-underline-94 MuiInputBase-formControl-104 MuiInput-formControl-91\\"><input aria-invalid=\\"false\\" class=\\"MuiInputBase-input-113 MuiInput-input-98 MuiInputBase-inputType-116 MuiInput-inputType-101\\" id=\\"login--form-password\\" placeholder=\\"Your strong password\\" required=\\"\\" type=\\"password\\" value=\\"\\"></div></div></div><div class=\\"MuiDialogActions-root-120 dialog-footer\\"><button tabindex=\\"0\\" class=\\"MuiButtonBase-root-148 MuiButton-root-122 MuiButton-text-124 MuiButton-flat-127 MuiButton-colorInherit-143 MuiDialogActions-action-121\\" type=\\"button\\" id=\\"login--form-cancel\\"><span class=\\"MuiButton-label-123\\">Cancel</span><span class=\\"MuiTouchRipple-root-151\\"></span></button><button tabindex=\\"0\\" class=\\"MuiButtonBase-root-148 MuiButton-root-122 MuiButton-text-124 MuiButton-flat-127 MuiButton-colorInherit-143 MuiDialogActions-action-121\\" type=\\"button\\" id=\\"login--form-submit\\"><span class=\\"MuiButton-label-123\\">Login</span><span class=\\"MuiTouchRipple-root-151\\"></span></button></div></div></div>"`;
|
||||
|
||||
exports[`<LoginModal /> should load the component with props 1`] = `"<div class=\\"MuiModal-root-13 MuiDialog-root-1 MuiDialog-scrollPaper-2\\" role=\\"dialog\\" id=\\"login--form-container\\" aria-labelledby=\\"login-dialog\\"><div class=\\"MuiBackdrop-root-15\\" aria-hidden=\\"true\\" style=\\"opacity: 1; -webkit-transition: opacity 225ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;\\"></div><div class=\\"MuiPaper-root-17 MuiPaper-elevation24-43 MuiPaper-rounded-18 MuiDialog-paper-4 MuiDialog-paperScrollPaper-5 MuiDialog-paperWidthXs-7 MuiDialog-paperFullWidth-11\\" style=\\"opacity: 1; -webkit-transition: opacity 225ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;\\" role=\\"document\\" tabindex=\\"-1\\"><div class=\\"MuiDialogTitle-root-44\\"><h2 class=\\"MuiTypography-root-45 MuiTypography-title-51\\">Login</h2></div><div class=\\"MuiDialogContent-root-71\\"><div class=\\"MuiTypography-root-45 MuiTypography-body1-54 MuiPaper-root-17 MuiPaper-elevation6-25 MuiSnackbarContent-root-158 loginError\\" role=\\"alertdialog\\" aria-describedby=\\"client-snackbar\\"><div class=\\"MuiSnackbarContent-message-159\\"><div id=\\"client-snackbar\\" class=\\"loginErrorMsg\\"><svg class=\\"MuiSvgIcon-root-161 loginIcon\\" focusable=\\"false\\" viewBox=\\"0 0 24 24\\" aria-hidden=\\"true\\" role=\\"presentation\\"><path fill=\\"none\\" d=\\"M0 0h24v24H0z\\"></path><path d=\\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z\\"></path></svg><span><div><strong>Error Title</strong></div><div>Error Description</div></span></div></div></div><div class=\\"MuiFormControl-root-72 MuiFormControl-fullWidth-75\\" aria-describedby=\\"username\\"><label class=\\"MuiFormLabel-root-83 MuiFormLabel-required-88 MuiInputLabel-root-76 MuiInputLabel-formControl-77 MuiInputLabel-animated-80\\" data-shrink=\\"false\\" for=\\"username\\">Username<span class=\\"MuiFormLabel-asterisk-89\\"> *</span></label><div class=\\"MuiInputBase-root-103 MuiInput-root-90 MuiInput-underline-94 MuiInputBase-formControl-104 MuiInput-formControl-91\\"><input aria-invalid=\\"false\\" class=\\"MuiInputBase-input-113 MuiInput-input-98\\" id=\\"login--form-username\\" placeholder=\\"Your username\\" required=\\"\\" type=\\"text\\" value=\\"\\"></div></div><div class=\\"MuiFormControl-root-72 MuiFormControl-fullWidth-75\\" aria-describedby=\\"password\\" style=\\"margin-top: 8px;\\"><label class=\\"MuiFormLabel-root-83 MuiFormLabel-required-88 MuiInputLabel-root-76 MuiInputLabel-formControl-77 MuiInputLabel-animated-80\\" data-shrink=\\"false\\" for=\\"password\\">Password<span class=\\"MuiFormLabel-asterisk-89\\"> *</span></label><div class=\\"MuiInputBase-root-103 MuiInput-root-90 MuiInput-underline-94 MuiInputBase-formControl-104 MuiInput-formControl-91\\"><input aria-invalid=\\"false\\" class=\\"MuiInputBase-input-113 MuiInput-input-98 MuiInputBase-inputType-116 MuiInput-inputType-101\\" id=\\"login--form-password\\" placeholder=\\"Your strong password\\" required=\\"\\" type=\\"password\\" value=\\"\\"></div></div></div><div class=\\"MuiDialogActions-root-120 dialog-footer\\"><button tabindex=\\"0\\" class=\\"MuiButtonBase-root-148 MuiButton-root-122 MuiButton-text-124 MuiButton-flat-127 MuiButton-colorInherit-143 MuiDialogActions-action-121\\" type=\\"button\\" id=\\"login--form-cancel\\"><span class=\\"MuiButton-label-123\\">Cancel</span><span class=\\"MuiTouchRipple-root-151\\"></span></button><button tabindex=\\"0\\" class=\\"MuiButtonBase-root-148 MuiButton-root-122 MuiButton-text-124 MuiButton-flat-127 MuiButton-colorInherit-143 MuiDialogActions-action-121\\" type=\\"button\\" id=\\"login--form-submit\\"><span class=\\"MuiButton-label-123\\">Login</span><span class=\\"MuiTouchRipple-root-151\\"></span></button></div></div></div>"`;
|
||||
exports[`<LoginModal /> should load the component with props 1`] = `"<div class=\\"MuiModal-root-13 MuiDialog-root-1 MuiDialog-scrollPaper-2\\" role=\\"dialog\\" id=\\"login--form-container\\"><div class=\\"MuiBackdrop-root-15\\" aria-hidden=\\"true\\" style=\\"opacity: 1; -webkit-transition: opacity 225ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;\\"></div><div class=\\"MuiPaper-root-17 MuiPaper-elevation24-43 MuiPaper-rounded-18 MuiDialog-paper-4 MuiDialog-paperScrollPaper-5 MuiDialog-paperWidthXs-7 MuiDialog-paperFullWidth-11\\" style=\\"opacity: 1; -webkit-transition: opacity 225ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;\\" role=\\"document\\" tabindex=\\"-1\\"><div class=\\"MuiDialogTitle-root-44\\"><h2 class=\\"MuiTypography-root-45 MuiTypography-title-51\\">Login</h2></div><div class=\\"MuiDialogContent-root-71\\"><div class=\\"MuiTypography-root-45 MuiTypography-body1-54 MuiPaper-root-17 MuiPaper-elevation6-25 MuiSnackbarContent-root-158 loginError\\" role=\\"alertdialog\\"><div class=\\"MuiSnackbarContent-message-159\\"><div id=\\"client-snackbar\\" class=\\"loginErrorMsg\\"><svg class=\\"MuiSvgIcon-root-161 loginIcon\\" focusable=\\"false\\" viewBox=\\"0 0 24 24\\" aria-hidden=\\"true\\" role=\\"presentation\\"><path fill=\\"none\\" d=\\"M0 0h24v24H0z\\"></path><path d=\\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z\\"></path></svg><span><div><strong>Error Title</strong></div><div>Error Description</div></span></div></div></div><div class=\\"MuiFormControl-root-72 MuiFormControl-fullWidth-75\\"><label class=\\"MuiFormLabel-root-83 MuiFormLabel-required-88 MuiInputLabel-root-76 MuiInputLabel-formControl-77 MuiInputLabel-animated-80\\" data-shrink=\\"false\\" for=\\"username\\">Username<span class=\\"MuiFormLabel-asterisk-89\\"> *</span></label><div class=\\"MuiInputBase-root-103 MuiInput-root-90 MuiInput-underline-94 MuiInputBase-formControl-104 MuiInput-formControl-91\\"><input aria-invalid=\\"false\\" class=\\"MuiInputBase-input-113 MuiInput-input-98\\" id=\\"login--form-username\\" placeholder=\\"Your username\\" required=\\"\\" type=\\"text\\" value=\\"\\"></div></div><div class=\\"MuiFormControl-root-72 MuiFormControl-fullWidth-75\\" style=\\"margin-top: 8px;\\"><label class=\\"MuiFormLabel-root-83 MuiFormLabel-required-88 MuiInputLabel-root-76 MuiInputLabel-formControl-77 MuiInputLabel-animated-80\\" data-shrink=\\"false\\" for=\\"password\\">Password<span class=\\"MuiFormLabel-asterisk-89\\"> *</span></label><div class=\\"MuiInputBase-root-103 MuiInput-root-90 MuiInput-underline-94 MuiInputBase-formControl-104 MuiInput-formControl-91\\"><input aria-invalid=\\"false\\" class=\\"MuiInputBase-input-113 MuiInput-input-98 MuiInputBase-inputType-116 MuiInput-inputType-101\\" id=\\"login--form-password\\" placeholder=\\"Your strong password\\" required=\\"\\" type=\\"password\\" value=\\"\\"></div></div></div><div class=\\"MuiDialogActions-root-120 dialog-footer\\"><button tabindex=\\"0\\" class=\\"MuiButtonBase-root-148 MuiButton-root-122 MuiButton-text-124 MuiButton-flat-127 MuiButton-colorInherit-143 MuiDialogActions-action-121\\" type=\\"button\\" id=\\"login--form-cancel\\"><span class=\\"MuiButton-label-123\\">Cancel</span><span class=\\"MuiTouchRipple-root-151\\"></span></button><button tabindex=\\"0\\" class=\\"MuiButtonBase-root-148 MuiButton-root-122 MuiButton-text-124 MuiButton-flat-127 MuiButton-colorInherit-143 MuiDialogActions-action-121\\" type=\\"button\\" id=\\"login--form-submit\\"><span class=\\"MuiButton-label-123\\">Login</span><span class=\\"MuiTouchRipple-root-151\\"></span></button></div></div></div>"`;
|
||||
|
@ -1,3 +1,3 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<NotFound /> component should set html from props 1`] = `"<div class=\\"notFound\\"><h1>Error 404 - verdaccio</h1><hr/><p>Oops, The package you are trying to access does not exist.</p></div>"`;
|
||||
exports[`<NotFound /> component should set html from props 1`] = `"<div class=\\"container content notFound\\"><h1>Error 404 - verdaccio</h1><hr/><p>Oops, The package you are trying to access does not exist.</p></div>"`;
|
||||
|
@ -1,3 +1,3 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<PackageList /> component should load the component with packages 1`] = `"<div class=\\"package-list-items\\"><div class=\\"pkgContainer\\"><h1 class=\\"listTitle\\">Available Packages</h1><li><section class=\\"package\\"><a href=\\"detail/verdaccio\\"><div class=\\"header\\"><div class=\\"title\\"><h1>verdaccio <div role=\\"button\\" class=\\"MuiChip-root-1\\" tabindex=\\"-1\\"><span class=\\"MuiChip-label-20\\">v1.0.0</span></div></h1></div><div role=\\"author\\" class=\\"author\\">By: Sam</div></div><div class=\\"footer\\"><p class=\\"description\\">Private NPM repository</p></div><div class=\\"tags\\"></div><div class=\\"details\\"><div class=\\"homepage\\">Published less than a minute ago</div><div class=\\"license\\"></div></div></a></section></li><li><section class=\\"package\\"><a href=\\"detail/abc\\"><div class=\\"header\\"><div class=\\"title\\"><h1>abc <div role=\\"button\\" class=\\"MuiChip-root-1\\" tabindex=\\"-1\\"><span class=\\"MuiChip-label-20\\">v1.0.1</span></div></h1></div><div role=\\"author\\" class=\\"author\\">By: Rose</div></div><div class=\\"footer\\"><p class=\\"description\\">abc description</p></div><div class=\\"tags\\"></div><div class=\\"details\\"><div class=\\"homepage\\">Published less than a minute ago</div><div class=\\"license\\"></div></div></a></section></li><li><section class=\\"package\\"><a href=\\"detail/xyz\\"><div class=\\"header\\"><div class=\\"title\\"><h1>xyz <div role=\\"button\\" class=\\"MuiChip-root-1\\" tabindex=\\"-1\\"><span class=\\"MuiChip-label-20\\">v1.1.0</span></div></h1></div><div role=\\"author\\" class=\\"author\\">By: Martin</div></div><div class=\\"footer\\"><p class=\\"description\\">xyz description</p></div><div class=\\"tags\\"></div><div class=\\"details\\"><div class=\\"homepage\\"></div><div class=\\"license\\"></div></div></a></section></li></div></div>"`;
|
||||
exports[`<PackageList /> component should load the component with packages 1`] = `"<div class=\\"package-list-items\\"><div class=\\"pkgContainer\\"><h1 class=\\"listTitle\\">Available Packages</h1><ul><li><section class=\\"package\\"><a href=\\"detail/undefined\\"><div class=\\"header\\"><div class=\\"title\\"><h1> <div role=\\"button\\" class=\\"MuiChip-root-1\\" tabindex=\\"-1\\"><span class=\\"MuiChip-label-20\\">v1.0.0</span></div></h1></div><div role=\\"author\\" class=\\"author\\">By: Sam</div></div><div class=\\"footer\\"><p class=\\"description\\">Private NPM repository</p></div><div class=\\"tags\\"></div><div class=\\"details\\"><div class=\\"homepage\\">Published less than a minute ago</div><div class=\\"license\\"></div></div></a></section></li><li><section class=\\"package\\"><a href=\\"detail/undefined\\"><div class=\\"header\\"><div class=\\"title\\"><h1> <div role=\\"button\\" class=\\"MuiChip-root-1\\" tabindex=\\"-1\\"><span class=\\"MuiChip-label-20\\">v1.0.1</span></div></h1></div><div role=\\"author\\" class=\\"author\\">By: Rose</div></div><div class=\\"footer\\"><p class=\\"description\\">abc description</p></div><div class=\\"tags\\"></div><div class=\\"details\\"><div class=\\"homepage\\">Published less than a minute ago</div><div class=\\"license\\"></div></div></a></section></li><li><section class=\\"package\\"><a href=\\"detail/undefined\\"><div class=\\"header\\"><div class=\\"title\\"><h1> <div role=\\"button\\" class=\\"MuiChip-root-1\\" tabindex=\\"-1\\"><span class=\\"MuiChip-label-20\\">v1.1.0</span></div></h1></div><div role=\\"author\\" class=\\"author\\">By: Martin</div></div><div class=\\"footer\\"><p class=\\"description\\">xyz description</p></div><div class=\\"tags\\"></div><div class=\\"details\\"><div class=\\"homepage\\"></div><div class=\\"license\\"></div></div></a></section></li></ul></div></div>"`;
|
||||
|
@ -1,3 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<Search /> component should match the snapshot 1`] = `"<form autoComplete=\\"off\\"><input type=\\"text\\" name=\\"search-box\\" placeholder=\\"Type to search...\\" class=\\"searchBox\\" autoComplete=\\"off\\"/></form>"`;
|
@ -17,12 +17,18 @@ describe('<Header /> component with logged in state', () => {
|
||||
handleLogout: jest.fn(),
|
||||
toggleLoginModal: jest.fn(),
|
||||
scope: 'test scope',
|
||||
withoutSearch: true,
|
||||
};
|
||||
wrapper = mount(<Header {...props} />);
|
||||
});
|
||||
|
||||
test('should load the component in logged in state', () => {
|
||||
const state = { openInfoDialog: false, registryUrl: 'http://localhost' };
|
||||
const state = {
|
||||
openInfoDialog: false,
|
||||
packages: undefined,
|
||||
registryUrl: 'http://localhost',
|
||||
showMobileNavBar: false,
|
||||
};
|
||||
expect(wrapper.state()).toEqual(state);
|
||||
expect(wrapper.html()).toMatchSnapshot();
|
||||
});
|
||||
@ -53,12 +59,18 @@ describe('<Header /> component with logged out state', () => {
|
||||
handleLogout: jest.fn(),
|
||||
toggleLoginModal: jest.fn(),
|
||||
scope: 'test scope',
|
||||
withoutSearch: true,
|
||||
};
|
||||
wrapper = mount(<Header {...props} />);
|
||||
});
|
||||
|
||||
test('should load the component in logged out state', () => {
|
||||
const state = { openInfoDialog: false, registryUrl: 'http://localhost' };
|
||||
const state = {
|
||||
openInfoDialog: false,
|
||||
packages: undefined,
|
||||
registryUrl: 'http://localhost',
|
||||
showMobileNavBar: false,
|
||||
};
|
||||
expect(wrapper.state()).toEqual(state);
|
||||
expect(wrapper.html()).toMatchSnapshot();
|
||||
});
|
||||
|
@ -5,8 +5,6 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import PackageList from '../../../../src/webui/components/PackageList/index';
|
||||
import Help from '../../../../src/webui/components/Help/index';
|
||||
import NoItems from '../../../../src/webui/components/NoItems/index';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
|
||||
describe('<PackageList /> component', () => {
|
||||
@ -18,12 +16,10 @@ describe('<PackageList /> component', () => {
|
||||
const wrapper = mount(
|
||||
<PackageList packages={props.packages} help={props.help} />
|
||||
);
|
||||
expect(wrapper.find('Help')).toHaveLength(1);
|
||||
|
||||
expect(wrapper.find('h1').text()).toEqual('No Package Published Yet');
|
||||
|
||||
const instance = wrapper.instance();
|
||||
expect(instance.isTherePackages()).toBeFalsy();
|
||||
expect(instance.renderHelp()).toBeTruthy();
|
||||
expect(instance.renderOptions()).toEqual(<Help />);
|
||||
expect(instance.renderTitle()).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should load the component with packages', () => {
|
||||
@ -52,23 +48,16 @@ describe('<PackageList /> component', () => {
|
||||
],
|
||||
help: false
|
||||
};
|
||||
|
||||
const wrapper = mount(
|
||||
<BrowserRouter>
|
||||
<PackageList packages={props.packages} help={props.help} />
|
||||
</BrowserRouter>
|
||||
);
|
||||
|
||||
const instance = wrapper.find(PackageList).instance();
|
||||
|
||||
expect(instance.isTherePackages()).toBeTruthy();
|
||||
expect(instance.renderHelp()).toBeUndefined();
|
||||
expect(instance.renderTitle().props.children).toEqual('Available Packages');
|
||||
expect(instance.renderNoItems()).toEqual(
|
||||
<NoItems className="package-no-items" text="No items were found with that query" />
|
||||
);
|
||||
expect(instance.renderOptions()).toEqual(
|
||||
<NoItems className="package-no-items" text="No items were found with that query" />
|
||||
);
|
||||
expect(wrapper.find('.listTitle').text()).toContain('Available Packages');
|
||||
|
||||
// package count
|
||||
expect(wrapper.find('Package')).toHaveLength(3);
|
||||
// match snapshot
|
||||
|
@ -1,45 +0,0 @@
|
||||
/**
|
||||
* Search component
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import Search from '../../../../src/webui/components/Search/index';
|
||||
console.error = jest.fn();
|
||||
|
||||
describe('<Search /> component', () => {
|
||||
it('should give error for the required fields', () => {
|
||||
const wrapper = shallow(<Search />);
|
||||
expect(console.error).toBeCalled();
|
||||
expect(wrapper.find('input').prop('placeholder')).toEqual(
|
||||
'Type to search...'
|
||||
);
|
||||
});
|
||||
|
||||
it('should have <input /> element with correct properties', () => {
|
||||
const props = {
|
||||
handleSearchInput: () => {},
|
||||
placeHolder: 'Test placeholder'
|
||||
};
|
||||
const wrapper = shallow(<Search {...props} />);
|
||||
expect(wrapper.find('input')).toHaveLength(1);
|
||||
expect(wrapper.find('input').prop('placeholder')).toEqual(
|
||||
'Test placeholder'
|
||||
);
|
||||
});
|
||||
|
||||
it('should call the handleSearchInput function', () => {
|
||||
const props = {
|
||||
handleSearchInput: jest.fn()
|
||||
};
|
||||
const wrapper = shallow(<Search {...props} />);
|
||||
wrapper.find('input').simulate('change');
|
||||
expect(props.handleSearchInput).toBeCalled();
|
||||
});
|
||||
|
||||
it('should match the snapshot', () => {
|
||||
const props = { handleSearchInput: () => {} };
|
||||
const wrapper = shallow(<Search {...props} />);
|
||||
expect(wrapper.html()).toMatchSnapshot();
|
||||
});
|
||||
});
|
@ -1,39 +0,0 @@
|
||||
/**
|
||||
* Home Component
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
import Home from '../../../../src/webui/modules/home/index';
|
||||
|
||||
describe('<Home /> Component', () => {
|
||||
let wrapper;
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = mount(<Home />);
|
||||
});
|
||||
|
||||
it('handleSearchInput - should match the search query', () => {
|
||||
const { handleSearchInput } = wrapper.instance();
|
||||
const result = 'test query string one';
|
||||
const input = {
|
||||
target: {
|
||||
value: result
|
||||
}
|
||||
};
|
||||
handleSearchInput(input);
|
||||
expect(wrapper.state('query')).toBe(result);
|
||||
});
|
||||
|
||||
it('handleSearchInput - should match the trimmed search query', () => {
|
||||
const { handleSearchInput } = wrapper.instance();
|
||||
const result = ' ';
|
||||
const input = {
|
||||
target: {
|
||||
value: result
|
||||
}
|
||||
};
|
||||
handleSearchInput(input);
|
||||
expect(wrapper.state('query')).toBe(result.trim());
|
||||
});
|
||||
});
|
BIN
yarn.lock
BIN
yarn.lock
Binary file not shown.
Loading…
Reference in New Issue
Block a user