mirror of
https://github.com/verdaccio/verdaccio.git
synced 2025-01-06 22:40:26 -05:00
fix: Fix 404 packages view, refactor readme and some components. Split them up for easy testing.
This commit is contained in:
parent
0f31b364e8
commit
d72ee76d74
15 changed files with 182 additions and 50 deletions
|
@ -1,14 +1,11 @@
|
|||
import React from 'react';
|
||||
import {HashRouter as Router, Route, Switch} from 'react-router-dom';
|
||||
import 'element-theme-default';
|
||||
import {i18n} from 'element-react';
|
||||
import locale from 'element-react/src/locale/lang/en';
|
||||
|
||||
i18n.use(locale);
|
||||
|
||||
import Header from './components/Header';
|
||||
import Home from './modules/home';
|
||||
import Detail from './modules/detail';
|
||||
import Route from './router';
|
||||
|
||||
import './styles/global.scss';
|
||||
import 'normalize.css';
|
||||
|
@ -16,17 +13,7 @@ import 'normalize.css';
|
|||
export default class App extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<Router>
|
||||
<div>
|
||||
<Header/>
|
||||
<div className="container">
|
||||
<Switch>
|
||||
<Route exact path="/(search/:keyword)?" component={ Home } />
|
||||
<Route path="/detail/:package" component={ Detail } />
|
||||
</Switch>
|
||||
</div>
|
||||
</div>
|
||||
</Router>
|
||||
<Route/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
14
src/webui/src/components/NotFound/404.scss
Normal file
14
src/webui/src/components/NotFound/404.scss
Normal file
|
@ -0,0 +1,14 @@
|
|||
@import '../../styles/variable';
|
||||
|
||||
.notFound {
|
||||
width: 100%;
|
||||
font-size: 18px;
|
||||
line-height: 30px;
|
||||
border: none;
|
||||
border-bottom: 1px solid lightgrey;
|
||||
outline: none;
|
||||
|
||||
&:focus {
|
||||
border-bottom: 1px solid grey;
|
||||
}
|
||||
}
|
23
src/webui/src/components/NotFound/index.js
Normal file
23
src/webui/src/components/NotFound/index.js
Normal file
|
@ -0,0 +1,23 @@
|
|||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import classes from './404.scss';
|
||||
|
||||
const NotFound = (props) => {
|
||||
return (
|
||||
<div className={classes.notFound}>
|
||||
<h1>Error 404 - {props.pkg}</h1>
|
||||
<hr/>
|
||||
<p>
|
||||
Oops, The package you are trying to access does not exist.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
NotFound.propTypes = {
|
||||
pkg: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
export default NotFound;
|
|
@ -18,9 +18,17 @@ export default class Package extends React.Component {
|
|||
return (
|
||||
<Link to={`detail/${pkg.name}`} className={classes.package}>
|
||||
<h1>{pkg.name}<Tag type="gray">v{pkg.version}</Tag></h1>
|
||||
<span role="author" className={classes.author}>By: {pkg.author.name}</span>
|
||||
{this.renderAuthor(pkg)}
|
||||
<p>{pkg.description}</p>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
renderAuthor(pkg) {
|
||||
if (!pkg.author.name) {
|
||||
return;
|
||||
}
|
||||
|
||||
return <span role="author" className={classes.author}>By: {pkg.author.name}</span>;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,10 @@ .package {
|
|||
top: 10px;
|
||||
right: 0;
|
||||
color: lightgrey;
|
||||
font-size: 12px;
|
||||
font-size: inherit;
|
||||
word-wrap: break-word;
|
||||
width: 100px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
p {
|
||||
|
|
35
src/webui/src/components/PackageDetail/index.js
Normal file
35
src/webui/src/components/PackageDetail/index.js
Normal file
|
@ -0,0 +1,35 @@
|
|||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import isNil from 'lodash/isNil';
|
||||
|
||||
import Readme from '../Readme';
|
||||
|
||||
import classes from './packageDetail.scss';
|
||||
|
||||
const PackageDetail = (props) => {
|
||||
|
||||
const displayState = (readMe) => {
|
||||
if (isNil(readMe)) {
|
||||
return;
|
||||
}
|
||||
return <Readme readMe={readMe}/>;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={classes.pkgDetail}>
|
||||
<h1 className={ classes.title }>{ props.package }</h1>
|
||||
<hr/>
|
||||
<div className={classes.readme}>
|
||||
{displayState(props.readMe)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
PackageDetail.propTypes = {
|
||||
readMe: PropTypes.string,
|
||||
package: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
export default PackageDetail;
|
14
src/webui/src/components/PackageDetail/packageDetail.scss
Normal file
14
src/webui/src/components/PackageDetail/packageDetail.scss
Normal file
|
@ -0,0 +1,14 @@
|
|||
@import '../../styles/variable';
|
||||
|
||||
.pkgDetail {
|
||||
|
||||
.title {
|
||||
font-size: 28px;
|
||||
color: $text-black;
|
||||
}
|
||||
|
||||
.readme {
|
||||
margin-bottom: 5em;
|
||||
}
|
||||
|
||||
}
|
15
src/webui/src/components/Readme/index.js
Normal file
15
src/webui/src/components/Readme/index.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import 'github-markdown-css';
|
||||
|
||||
const Readme = (props) => {
|
||||
return <div className="markdown-body" dangerouslySetInnerHTML={{__html: props.readMe}}/>;
|
||||
};
|
||||
|
||||
Readme.propTypes = {
|
||||
readMe: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
export default Readme;
|
14
src/webui/src/components/Readme/readme.scss
Normal file
14
src/webui/src/components/Readme/readme.scss
Normal file
|
@ -0,0 +1,14 @@
|
|||
@import '../../styles/variable';
|
||||
|
||||
.searchBox {
|
||||
width: 100%;
|
||||
font-size: 18px;
|
||||
line-height: 30px;
|
||||
border: none;
|
||||
border-bottom: 1px solid lightgrey;
|
||||
outline: none;
|
||||
|
||||
&:focus {
|
||||
border-bottom: 1px solid grey;
|
||||
}
|
||||
}
|
|
@ -8,15 +8,20 @@ const Search = (props) => {
|
|||
return (
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Type to search..."
|
||||
placeholder={props.placeHolder}
|
||||
className={ classes.searchBox }
|
||||
onChange={ props.handleSearchInput }
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
Search.defaultProps = {
|
||||
placeHolder: 'Type to search...'
|
||||
};
|
||||
|
||||
Search.propTypes = {
|
||||
handleSearchInput: PropTypes.func.isRequired
|
||||
handleSearchInput: PropTypes.func.isRequired,
|
||||
placeHolder: PropTypes.string,
|
||||
};
|
||||
|
||||
export default Search;
|
||||
|
|
|
@ -1,6 +1,2 @@
|
|||
@import '../../styles/variable';
|
||||
|
||||
.title {
|
||||
font-size: 28px;
|
||||
color: $text-black;
|
||||
}
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
import React from 'react';
|
||||
import API from '../../../utils/api';
|
||||
import {Loading} from 'element-react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {Loading} from 'element-react';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
|
||||
import classes from './detail.scss';
|
||||
import 'github-markdown-css';
|
||||
import PackageDetail from '../../components/PackageDetail';
|
||||
import NotFound from '../../components/NotFound';
|
||||
import API from '../../../utils/api';
|
||||
|
||||
const loadingMessage = 'Loading...';
|
||||
|
||||
export default class Detail extends React.Component {
|
||||
static propTypes = {
|
||||
|
@ -12,41 +15,30 @@ export default class Detail extends React.Component {
|
|||
}
|
||||
|
||||
state = {
|
||||
readMe: ''
|
||||
readMe: '',
|
||||
notFound: false,
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
try {
|
||||
let resp = await API.get(`package/readme/${this.props.match.params.package}`);
|
||||
const resp = await API.get(`package/readme/${this.props.match.params.package}`);
|
||||
this.setState({
|
||||
readMe: resp.data
|
||||
});
|
||||
} catch (err) {
|
||||
this.setState({
|
||||
readMe: 'Failed to load readme...'
|
||||
notFound: true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
renderReadMe() {
|
||||
if (this.state.readMe) {
|
||||
return (
|
||||
<div className="markdown-body" dangerouslySetInnerHTML={{__html: this.state.readMe}}/>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Loading text="Loading..." />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h1 className={ classes.title }>{ this.props.match.params.package }</h1>
|
||||
<hr/>
|
||||
{this.renderReadMe()}
|
||||
</div>
|
||||
);
|
||||
if (this.state.notFound) {
|
||||
return <NotFound
|
||||
pkg={this.props.match.params.package}/>;
|
||||
} else if (isEmpty(this.state.readMe)) {
|
||||
return <Loading text={loadingMessage} />;
|
||||
}
|
||||
return <PackageDetail readMe={this.state.readMe} package={this.props.match.params.package}/>;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,8 @@ export default class Home extends React.Component {
|
|||
this.loadPackages();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
|
||||
componentDidUpdate(prevProps, prevState) { // eslint-disable-line no-unused-vars
|
||||
if (prevState.query !== this.state.query) {
|
||||
if (this.req && this.req.abort) this.req.abort();
|
||||
this.setState({
|
||||
|
|
24
src/webui/src/router.js
Normal file
24
src/webui/src/router.js
Normal file
|
@ -0,0 +1,24 @@
|
|||
import React from 'react';
|
||||
import {HashRouter as Router, Route, Switch} from 'react-router-dom';
|
||||
|
||||
import Header from './components/Header';
|
||||
import Home from './modules/home';
|
||||
import Detail from './modules/detail';
|
||||
|
||||
const RouterApp = () => {
|
||||
return (
|
||||
<Router>
|
||||
<div>
|
||||
<Header/>
|
||||
<div className="container">
|
||||
<Switch>
|
||||
<Route exact path="/(search/:keyword)?" component={ Home } />
|
||||
<Route path="/detail/:package" component={Detail} />
|
||||
</Switch>
|
||||
</div>
|
||||
</div>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
|
||||
export default RouterApp;
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
body {
|
||||
font-family: $font;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
:global {
|
||||
|
|
Loading…
Reference in a new issue