forked from sombochea/verdaccio-ui
Merge pull request #77 from griffithtp/refactor/74_linting-warnings
refactor: 74 linting warnings
This commit is contained in:
commit
a0f0c80e2e
@ -39,8 +39,8 @@ const register = (url, method = 'get', options = {}) => {
|
|||||||
* Bind API methods
|
* Bind API methods
|
||||||
*/
|
*/
|
||||||
class API {
|
class API {
|
||||||
request() {
|
public request(...rest) {
|
||||||
return register.call(null, ...arguments);
|
return register.call(null, ...rest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,6 @@
|
|||||||
* Mock response for logo api
|
* Mock response for logo api
|
||||||
* @returns {promise}
|
* @returns {promise}
|
||||||
*/
|
*/
|
||||||
export default function() {
|
export default function<T>(): Promise<T> {
|
||||||
return Promise.resolve('http://localhost/-/static/logo.png');
|
return Promise.resolve('http://localhost/-/static/logo.png');
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import { generateTokenWithTimeRange } from '../../jest/unit/components/__mocks__
|
|||||||
|
|
||||||
jest.mock('../utils/storage', () => {
|
jest.mock('../utils/storage', () => {
|
||||||
class LocalStorageMock {
|
class LocalStorageMock {
|
||||||
store: object;
|
private store: object;
|
||||||
public constructor() {
|
public constructor() {
|
||||||
this.store = {};
|
this.store = {};
|
||||||
}
|
}
|
||||||
@ -43,7 +43,7 @@ describe('App', () => {
|
|||||||
expect(wrapper.state().showLoginModal).toBeFalsy();
|
expect(wrapper.state().showLoginModal).toBeFalsy();
|
||||||
handleToggleLoginModal();
|
handleToggleLoginModal();
|
||||||
expect(wrapper.state('showLoginModal')).toBeTruthy();
|
expect(wrapper.state('showLoginModal')).toBeTruthy();
|
||||||
expect(wrapper.state('error')).toEqual({});
|
expect(wrapper.state('error')).toEqual(undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('isUserAlreadyLoggedIn: token already available in storage', async () => {
|
test('isUserAlreadyLoggedIn: token already available in storage', async () => {
|
||||||
|
@ -14,14 +14,26 @@ import '../styles/typeface-roboto.scss';
|
|||||||
import '../styles/main.scss';
|
import '../styles/main.scss';
|
||||||
import 'normalize.css';
|
import 'normalize.css';
|
||||||
import Footer from '../components/Footer';
|
import Footer from '../components/Footer';
|
||||||
|
import { FormError } from 'src/components/Login/Login';
|
||||||
|
|
||||||
export const AppContext = React.createContext<null>(null);
|
export const AppContext = React.createContext<{}>({});
|
||||||
export const AppContextProvider = AppContext.Provider;
|
export const AppContextProvider = AppContext.Provider;
|
||||||
export const AppContextConsumer = AppContext.Consumer;
|
export const AppContextConsumer = AppContext.Consumer;
|
||||||
|
|
||||||
export default class App extends Component<any, any> {
|
export interface AppStateInterface {
|
||||||
public state = {
|
error?: FormError;
|
||||||
error: {},
|
logoUrl: string;
|
||||||
|
user: {
|
||||||
|
username?: string;
|
||||||
|
};
|
||||||
|
scope: string;
|
||||||
|
showLoginModal: boolean;
|
||||||
|
isUserLoggedIn: boolean;
|
||||||
|
packages: [];
|
||||||
|
isLoading: boolean;
|
||||||
|
}
|
||||||
|
export default class App extends Component<{}, AppStateInterface> {
|
||||||
|
public state: AppStateInterface = {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
logoUrl: window.VERDACCIO_LOGO,
|
logoUrl: window.VERDACCIO_LOGO,
|
||||||
user: {},
|
user: {},
|
||||||
@ -49,7 +61,7 @@ export default class App extends Component<any, any> {
|
|||||||
public render(): React.ReactElement<HTMLDivElement> {
|
public render(): React.ReactElement<HTMLDivElement> {
|
||||||
const { isLoading, isUserLoggedIn, packages, logoUrl, user, scope } = this.state;
|
const { isLoading, isUserLoggedIn, packages, logoUrl, user, scope } = this.state;
|
||||||
|
|
||||||
const context: any = { isUserLoggedIn, packages, logoUrl, user, scope };
|
const context = { isUserLoggedIn, packages, logoUrl, user, scope };
|
||||||
|
|
||||||
return (
|
return (
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@ -112,7 +124,6 @@ export default class App extends Component<any, any> {
|
|||||||
this.setState(prevState => ({
|
this.setState(prevState => ({
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
showLoginModal: !prevState.showLoginModal,
|
showLoginModal: !prevState.showLoginModal,
|
||||||
error: {},
|
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ const ACTIONS = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
class ActionBar extends Component<any, any> {
|
class ActionBar extends Component {
|
||||||
public render(): ReactElement<HTMLElement> {
|
public render(): ReactElement<HTMLElement> {
|
||||||
return (
|
return (
|
||||||
<DetailContextConsumer>
|
<DetailContextConsumer>
|
||||||
@ -36,7 +36,7 @@ class ActionBar extends Component<any, any> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderIconsWithLink(link: string, component: any): ReactElement<HTMLElement> {
|
private renderIconsWithLink(link: string, component: JSX.Element): ReactElement<HTMLElement> {
|
||||||
return (
|
return (
|
||||||
<a href={link} target={'_blank'}>
|
<a href={link} target={'_blank'}>
|
||||||
{component}
|
{component}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { Component, ReactNode } from 'react';
|
import React, { Component, ReactNode, ReactElement } from 'react';
|
||||||
|
|
||||||
import Avatar from '@material-ui/core/Avatar';
|
import Avatar from '@material-ui/core/Avatar';
|
||||||
import List from '@material-ui/core/List';
|
import List from '@material-ui/core/List';
|
||||||
@ -8,18 +8,18 @@ import { DetailContextConsumer } from '../../pages/version/Version';
|
|||||||
import { Heading, AuthorListItem } from './styles';
|
import { Heading, AuthorListItem } from './styles';
|
||||||
import { isEmail } from '../../utils/url';
|
import { isEmail } from '../../utils/url';
|
||||||
|
|
||||||
class Authors extends Component<any, any> {
|
class Authors extends Component {
|
||||||
render() {
|
public render(): ReactElement<HTMLElement> {
|
||||||
return (
|
return (
|
||||||
<DetailContextConsumer>
|
<DetailContextConsumer>
|
||||||
{(context: any) => {
|
{context => {
|
||||||
return context && context.packageMeta && this.renderAuthor(context.packageMeta);
|
return context && context.packageMeta && this.renderAuthor(context.packageMeta);
|
||||||
}}
|
}}
|
||||||
</DetailContextConsumer>
|
</DetailContextConsumer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderLinkForMail(email: string, avatarComponent: ReactNode, packageName: string, version: string) {
|
public renderLinkForMail(email: string, avatarComponent: ReactNode, packageName: string, version: string): ReactElement<HTMLElement> | ReactNode {
|
||||||
if (!email || isEmail(email) === false) {
|
if (!email || isEmail(email) === false) {
|
||||||
return avatarComponent;
|
return avatarComponent;
|
||||||
}
|
}
|
||||||
@ -31,7 +31,7 @@ class Authors extends Component<any, any> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderAuthor = packageMeta => {
|
public renderAuthor = packageMeta => {
|
||||||
const { author, name: packageName, version } = packageMeta.latest;
|
const { author, name: packageName, version } = packageMeta.latest;
|
||||||
|
|
||||||
if (!author) {
|
if (!author) {
|
||||||
|
@ -7,8 +7,8 @@ import MenuItem from '@material-ui/core/MenuItem';
|
|||||||
import { fontWeight } from '../../utils/styles/sizes';
|
import { fontWeight } from '../../utils/styles/sizes';
|
||||||
import { Wrapper, InputField, SuggestionContainer } from './styles';
|
import { Wrapper, InputField, SuggestionContainer } from './styles';
|
||||||
|
|
||||||
export interface Props {
|
interface Props {
|
||||||
suggestions: any[];
|
suggestions: unknown[];
|
||||||
suggestionsLoading?: boolean;
|
suggestionsLoading?: boolean;
|
||||||
suggestionsLoaded?: boolean;
|
suggestionsLoaded?: boolean;
|
||||||
suggestionsError?: boolean;
|
suggestionsError?: boolean;
|
||||||
@ -16,17 +16,17 @@ export interface Props {
|
|||||||
color?: string;
|
color?: string;
|
||||||
value?: string;
|
value?: string;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
startAdornment?: any;
|
startAdornment?: JSX.Element;
|
||||||
disableUnderline?: boolean;
|
disableUnderline?: boolean;
|
||||||
onChange?: (event: KeyboardEvent<HTMLInputElement>, { newValue, method }: { newValue: string; method: string }) => void;
|
onChange?: (event: KeyboardEvent<HTMLInputElement>, { newValue, method }: { newValue: string; method: string }) => void;
|
||||||
onSuggestionsFetch?: ({ value: string }) => Promise<void>;
|
onSuggestionsFetch?: ({ value: string }) => Promise<void>;
|
||||||
onCleanSuggestions?: () => void;
|
onCleanSuggestions?: () => void;
|
||||||
onClick?: (event: KeyboardEvent<HTMLInputElement>, { suggestionValue, method }: { suggestionValue: any[]; method: string }) => void;
|
onClick?: (event: KeyboardEvent<HTMLInputElement>, { suggestionValue, method }: { suggestionValue: string[]; method: string }) => void;
|
||||||
onKeyDown?: (event: KeyboardEvent<HTMLInputElement>) => void;
|
onKeyDown?: (event: KeyboardEvent<HTMLInputElement>) => void;
|
||||||
onBlur?: (event: KeyboardEvent<HTMLInputElement>) => void;
|
onBlur?: (event: KeyboardEvent<HTMLInputElement>) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderInputComponent = inputProps => {
|
const renderInputComponent = (inputProps): JSX.Element => {
|
||||||
const { ref, startAdornment, disableUnderline, onKeyDown, ...others } = inputProps;
|
const { ref, startAdornment, disableUnderline, onKeyDown, ...others } = inputProps;
|
||||||
return (
|
return (
|
||||||
<InputField
|
<InputField
|
||||||
@ -46,7 +46,7 @@ const renderInputComponent = inputProps => {
|
|||||||
|
|
||||||
const getSuggestionValue = (suggestion): string => suggestion.name;
|
const getSuggestionValue = (suggestion): string => suggestion.name;
|
||||||
|
|
||||||
const renderSuggestion = (suggestion, { query, isHighlighted }) => {
|
const renderSuggestion = (suggestion, { query, isHighlighted }): JSX.Element => {
|
||||||
const matches = match(suggestion.name, query);
|
const matches = match(suggestion.name, query);
|
||||||
const parts = parse(suggestion.name, matches);
|
const parts = parse(suggestion.name, matches);
|
||||||
return (
|
return (
|
||||||
@ -68,7 +68,7 @@ const renderSuggestion = (suggestion, { query, isHighlighted }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderMessage = message => {
|
const renderMessage = (message): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<MenuItem component="div" selected={false}>
|
<MenuItem component="div" selected={false}>
|
||||||
<div>{message}</div>
|
<div>{message}</div>
|
||||||
@ -98,7 +98,7 @@ const AutoComplete = ({
|
|||||||
suggestionsLoading = false,
|
suggestionsLoading = false,
|
||||||
suggestionsLoaded = false,
|
suggestionsLoaded = false,
|
||||||
suggestionsError = false,
|
suggestionsError = false,
|
||||||
}: Props) => {
|
}: Props): JSX.Element => {
|
||||||
const autosuggestProps = {
|
const autosuggestProps = {
|
||||||
renderInputComponent,
|
renderInputComponent,
|
||||||
suggestions,
|
suggestions,
|
||||||
@ -119,7 +119,7 @@ const AutoComplete = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
// this format avoid arrow function eslint rule
|
// this format avoid arrow function eslint rule
|
||||||
function renderSuggestionsContainer({ containerProps, children, query }) {
|
function renderSuggestionsContainer({ containerProps, children, query }): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<SuggestionContainer {...containerProps} square={true}>
|
<SuggestionContainer {...containerProps} square={true}>
|
||||||
{suggestionsLoaded && children === null && query && renderMessage(SUGGESTIONS_RESPONSE.NO_RESULT)}
|
{suggestionsLoaded && children === null && query && renderMessage(SUGGESTIONS_RESPONSE.NO_RESULT)}
|
||||||
|
@ -12,7 +12,7 @@ interface Props {
|
|||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderText: React.FC<any> = (text: string, children: React.ReactNode): React.ReactElement<HTMLElement> => {
|
const renderText = (text, children): JSX.Element => {
|
||||||
if (children) {
|
if (children) {
|
||||||
return <ClipBoardCopyText>{children}</ClipBoardCopyText>;
|
return <ClipBoardCopyText>{children}</ClipBoardCopyText>;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React, { Component, Fragment, ReactElement } from 'react';
|
import React, { Component, Fragment, ReactElement } from 'react';
|
||||||
import { withRouter } from 'react-router-dom';
|
import { withRouter, RouteProps } from 'react-router-dom';
|
||||||
import CardContent from '@material-ui/core/CardContent';
|
import CardContent from '@material-ui/core/CardContent';
|
||||||
|
|
||||||
import { DetailContextConsumer, VersionPageConsumerProps } from '../../pages/version/Version';
|
import { DetailContextConsumer, VersionPageConsumerProps } from '../../pages/version/Version';
|
||||||
@ -7,8 +7,19 @@ import { DetailContextConsumer, VersionPageConsumerProps } from '../../pages/ver
|
|||||||
import { CardWrap, Heading, Tags, Tag } from './styles';
|
import { CardWrap, Heading, Tags, Tag } from './styles';
|
||||||
import NoItems from '../NoItems';
|
import NoItems from '../NoItems';
|
||||||
|
|
||||||
class DepDetail extends Component<any, any> {
|
interface DepDetailProps {
|
||||||
constructor(props: any) {
|
name: string;
|
||||||
|
version: string;
|
||||||
|
onLoading: () => void;
|
||||||
|
history: string[];
|
||||||
|
}
|
||||||
|
interface DepDetailState {
|
||||||
|
name: string;
|
||||||
|
version: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class DepDetail extends Component<DepDetailProps & RouteProps, DepDetailState> {
|
||||||
|
constructor(props: DepDetailProps) {
|
||||||
super(props);
|
super(props);
|
||||||
const { name, version } = this.props;
|
const { name, version } = this.props;
|
||||||
|
|
||||||
@ -33,16 +44,16 @@ class DepDetail extends Component<any, any> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const WrapperDependencyDetail = withRouter(DepDetail);
|
const WrapperDependencyDetail = withRouter<any>(DepDetail);
|
||||||
|
|
||||||
class DependencyBlock extends Component<any, any> {
|
class DependencyBlock extends Component<{ title: string; dependencies: [] }> {
|
||||||
public render(): ReactElement<HTMLElement> {
|
public render(): ReactElement<HTMLElement> {
|
||||||
const { dependencies, title } = this.props;
|
const { dependencies, title } = this.props;
|
||||||
const deps = Object.entries(dependencies);
|
const deps = Object.entries(dependencies) as [];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DetailContextConsumer>
|
<DetailContextConsumer>
|
||||||
{({ enableLoading }: any) => {
|
{({ enableLoading }) => {
|
||||||
return (
|
return (
|
||||||
<CardWrap>
|
<CardWrap>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
@ -56,15 +67,15 @@ class DependencyBlock extends Component<any, any> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderTags = (deps: any, enableLoading: any) =>
|
private renderTags = (deps: [], enableLoading?: () => void) =>
|
||||||
deps.map(dep => {
|
deps.map(dep => {
|
||||||
const [name, version] = dep;
|
const [name, version] = dep as [string, string];
|
||||||
|
|
||||||
return <WrapperDependencyDetail key={name} name={name} onLoading={enableLoading} version={version} />;
|
return <WrapperDependencyDetail key={name} name={name} onLoading={enableLoading} version={version} />;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
class Dependencies extends Component<any, any> {
|
class Dependencies extends Component {
|
||||||
public state = {
|
public state = {
|
||||||
tabPosition: 0,
|
tabPosition: 0,
|
||||||
};
|
};
|
||||||
@ -79,7 +90,7 @@ class Dependencies extends Component<any, any> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private checkDependencyLength(dependency: Record<string, any> = {}): boolean {
|
private checkDependencyLength<T>(dependency: Record<string, T> = {}): boolean {
|
||||||
return Object.keys(dependency).length > 0;
|
return Object.keys(dependency).length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ interface DetailContainerState {
|
|||||||
tabPosition: number;
|
tabPosition: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
class DetailContainer extends Component<any, DetailContainerState> {
|
class DetailContainer<P> extends Component<P, DetailContainerState> {
|
||||||
public state = {
|
public state = {
|
||||||
tabPosition: 0,
|
tabPosition: 0,
|
||||||
};
|
};
|
||||||
@ -29,7 +29,7 @@ class DetailContainer extends Component<any, DetailContainerState> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleChange = (event: any, tabPosition: number) => {
|
private handleChange = (event: React.ChangeEvent<{}>, tabPosition: number) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
this.setState({ tabPosition });
|
this.setState({ tabPosition });
|
||||||
};
|
};
|
||||||
|
@ -11,18 +11,21 @@ import { isEmail } from '../../utils/url';
|
|||||||
interface Props {
|
interface Props {
|
||||||
type: 'contributors' | 'maintainers';
|
type: 'contributors' | 'maintainers';
|
||||||
}
|
}
|
||||||
|
interface State {
|
||||||
|
visibleDevs: number;
|
||||||
|
}
|
||||||
|
|
||||||
class Developers extends Component<Props, any> {
|
class Developers extends Component<Props, State> {
|
||||||
state = {
|
public state = {
|
||||||
visibleDevs: 6,
|
visibleDevs: 6,
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
public render(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<DetailContextConsumer>
|
<DetailContextConsumer>
|
||||||
{({ packageMeta }: any) => {
|
{({ packageMeta }) => {
|
||||||
const { type } = this.props;
|
const { type } = this.props;
|
||||||
const developerType = packageMeta.latest[type];
|
const developerType = packageMeta && packageMeta.latest[type];
|
||||||
if (!developerType || developerType.length === 0) return null;
|
if (!developerType || developerType.length === 0) return null;
|
||||||
return this.renderDevelopers(developerType, packageMeta);
|
return this.renderDevelopers(developerType, packageMeta);
|
||||||
}}
|
}}
|
||||||
@ -30,11 +33,11 @@ class Developers extends Component<Props, any> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleLoadMore = () => {
|
public handleLoadMore = () => {
|
||||||
this.setState(prev => ({ visibleDevs: prev.visibleDevs + 6 }));
|
this.setState(prev => ({ visibleDevs: prev.visibleDevs + 6 }));
|
||||||
};
|
};
|
||||||
|
|
||||||
renderDevelopers = (developers, packageMeta) => {
|
private renderDevelopers = (developers, packageMeta) => {
|
||||||
const { type } = this.props;
|
const { type } = this.props;
|
||||||
const { visibleDevs } = this.state;
|
const { visibleDevs } = this.state;
|
||||||
return (
|
return (
|
||||||
@ -54,7 +57,7 @@ class Developers extends Component<Props, any> {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
renderLinkForMail(email, avatarComponent, packageName, version) {
|
private renderLinkForMail(email, avatarComponent, packageName, version): JSX.Element {
|
||||||
if (!email || isEmail(email) === false) {
|
if (!email || isEmail(email) === false) {
|
||||||
return avatarComponent;
|
return avatarComponent;
|
||||||
}
|
}
|
||||||
@ -65,7 +68,7 @@ class Developers extends Component<Props, any> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderDeveloperDetails = ({ name, avatar, email }, packageMeta) => {
|
private renderDeveloperDetails = ({ name, avatar, email }, packageMeta) => {
|
||||||
const { name: packageName, version } = packageMeta.latest;
|
const { name: packageName, version } = packageMeta.latest;
|
||||||
|
|
||||||
const avatarComponent = <Avatar aria-label={name} src={avatar} />;
|
const avatarComponent = <Avatar aria-label={name} src={avatar} />;
|
||||||
|
@ -2,22 +2,23 @@ import React, { Component } from 'react';
|
|||||||
|
|
||||||
import List from '@material-ui/core/List';
|
import List from '@material-ui/core/List';
|
||||||
|
|
||||||
import { DetailContextConsumer } from '../../pages/version/Version';
|
import { DetailContextConsumer, VersionPageConsumerProps } from '../../pages/version/Version';
|
||||||
import { Heading, DistListItem, DistChips } from './styles';
|
import { Heading, DistListItem, DistChips } from './styles';
|
||||||
import fileSizeSI from '../../utils/file-size';
|
import fileSizeSI from '../../utils/file-size';
|
||||||
|
import { PackageMetaInterface } from 'types/packageMeta';
|
||||||
|
|
||||||
class Dist extends Component<any, any> {
|
class Dist extends Component {
|
||||||
render() {
|
public render(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<DetailContextConsumer>
|
<DetailContextConsumer>
|
||||||
{(context: any) => {
|
{(context: Partial<VersionPageConsumerProps>) => {
|
||||||
return this.renderDist(context);
|
return context && context.packageMeta && this.renderDist(context.packageMeta);
|
||||||
}}
|
}}
|
||||||
</DetailContextConsumer>
|
</DetailContextConsumer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderChips(dist: any, license: string) {
|
private renderChips(dist, license: string): JSX.Element | never[] {
|
||||||
const distDict = {
|
const distDict = {
|
||||||
'file-count': dist.fileCount,
|
'file-count': dist.fileCount,
|
||||||
size: dist.unpackedSize && fileSizeSI(dist.unpackedSize),
|
size: dist.unpackedSize && fileSizeSI(dist.unpackedSize),
|
||||||
@ -43,8 +44,8 @@ class Dist extends Component<any, any> {
|
|||||||
return chipsList;
|
return chipsList;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderDist = ({ packageMeta }: any) => {
|
private renderDist = (packageMeta: PackageMetaInterface) => {
|
||||||
const { dist = {}, license } = packageMeta.latest;
|
const { dist, license } = packageMeta && packageMeta.latest;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<List subheader={<Heading variant="subheading">{'Latest Distribution'}</Heading>}>
|
<List subheader={<Heading variant="subheading">{'Latest Distribution'}</Heading>}>
|
||||||
|
@ -3,7 +3,7 @@ import React from 'react';
|
|||||||
import { Wrapper, Left, Right, Earth, Flags, Love, Flag, Logo, Inner, ToolTip } from './styles';
|
import { Wrapper, Left, Right, Earth, Flags, Love, Flag, Logo, Inner, ToolTip } from './styles';
|
||||||
import { goToVerdaccioWebsite } from '../../utils/windows';
|
import { goToVerdaccioWebsite } from '../../utils/windows';
|
||||||
|
|
||||||
const renderTooltip = () => (
|
const renderTooltip = (): JSX.Element => (
|
||||||
<ToolTip>
|
<ToolTip>
|
||||||
<Earth name="earth" size="md" />
|
<Earth name="earth" size="md" />
|
||||||
<Flags>
|
<Flags>
|
||||||
@ -22,7 +22,7 @@ const ON_LABEL = 'on';
|
|||||||
const HEARTH_EMOJI = '♥';
|
const HEARTH_EMOJI = '♥';
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const renderRight = (version = window.VERDACCIO_VERSION) => {
|
const renderRight = (version = window.VERDACCIO_VERSION): JSX.Element => {
|
||||||
return (
|
return (
|
||||||
<Right>
|
<Right>
|
||||||
{POWERED_LABEL}
|
{POWERED_LABEL}
|
||||||
@ -32,7 +32,7 @@ const renderRight = (version = window.VERDACCIO_VERSION) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderLeft = () => (
|
const renderLeft = (): JSX.Element => (
|
||||||
<Left>
|
<Left>
|
||||||
{MADEWITH_LABEL}
|
{MADEWITH_LABEL}
|
||||||
<Love>{HEARTH_EMOJI}</Love>
|
<Love>{HEARTH_EMOJI}</Love>
|
||||||
|
@ -22,7 +22,7 @@ import RegistryInfoContent from '../RegistryInfoContent/RegistryInfoContent';
|
|||||||
import { Greetings, NavBar, InnerNavBar, MobileNavBar, InnerMobileNavBar, LeftSide, RightSide, IconSearchButton, SearchWrapper } from './styles';
|
import { Greetings, NavBar, InnerNavBar, MobileNavBar, InnerMobileNavBar, LeftSide, RightSide, IconSearchButton, SearchWrapper } from './styles';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
logo: string;
|
logo?: string;
|
||||||
username?: string;
|
username?: string;
|
||||||
onLogout: () => void;
|
onLogout: () => void;
|
||||||
onToggleLoginModal: () => void;
|
onToggleLoginModal: () => void;
|
||||||
@ -31,7 +31,7 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
anchorEl?: any;
|
anchorEl?: null | HTMLElement | ((element: HTMLElement) => HTMLElement);
|
||||||
openInfoDialog: boolean;
|
openInfoDialog: boolean;
|
||||||
registryUrl: string;
|
registryUrl: string;
|
||||||
showMobileNavBar: boolean;
|
showMobileNavBar: boolean;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import React, { MouseEvent } from 'react';
|
import React, { MouseEvent } from 'react';
|
||||||
import capitalize from 'lodash/capitalize';
|
import capitalize from 'lodash/capitalize';
|
||||||
|
import { Breakpoint } from '@material-ui/core/styles/createBreakpoints';
|
||||||
|
|
||||||
import { Svg, Img, ImgWrapper } from './styles';
|
import { Svg, Img, ImgWrapper } from './styles';
|
||||||
|
|
||||||
@ -57,10 +58,10 @@ export interface Props {
|
|||||||
name: keyof IconsMap;
|
name: keyof IconsMap;
|
||||||
className?: string;
|
className?: string;
|
||||||
onClick?: (event: MouseEvent<SVGElement | HTMLSpanElement>) => void;
|
onClick?: (event: MouseEvent<SVGElement | HTMLSpanElement>) => void;
|
||||||
size?: 'sm' | 'md';
|
size?: Breakpoint;
|
||||||
pointer?: boolean;
|
pointer?: boolean;
|
||||||
img?: boolean;
|
img?: boolean;
|
||||||
modifiers?: any;
|
modifiers?: null | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Icon: React.FC<Props> = ({ className, name, size = 'sm', img = false, pointer = false, ...props }) => {
|
const Icon: React.FC<Props> = ({ className, name, size = 'sm', img = false, pointer = false, ...props }) => {
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
import styled, { css } from 'react-emotion';
|
import styled, { css } from 'react-emotion';
|
||||||
|
import { Breakpoint } from '@material-ui/core/styles/createBreakpoints';
|
||||||
|
import { StyledOtherComponent } from 'create-emotion-styled';
|
||||||
|
import { DetailedHTMLProps, HTMLAttributes } from 'react';
|
||||||
|
|
||||||
const getSize = (size?: 'md' | 'sm') => {
|
const getSize = (size: Breakpoint): string => {
|
||||||
switch (size) {
|
switch (size) {
|
||||||
case 'md':
|
case 'md':
|
||||||
return `
|
return `
|
||||||
@ -15,7 +18,7 @@ const getSize = (size?: 'md' | 'sm') => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const commonStyle = ({ size = 'sm', pointer, modifiers }: any) => css`
|
const commonStyle = ({ size = 'sm' as Breakpoint, pointer, modifiers = null }): string => css`
|
||||||
&& {
|
&& {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
cursor: ${pointer ? 'pointer' : 'default'};
|
cursor: ${pointer ? 'pointer' : 'default'};
|
||||||
@ -30,7 +33,16 @@ export const Svg = styled('svg')`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const ImgWrapper = styled('span')`
|
export const ImgWrapper: StyledOtherComponent<
|
||||||
|
{
|
||||||
|
size?: Breakpoint;
|
||||||
|
pointer: boolean;
|
||||||
|
modifiers?: null | undefined;
|
||||||
|
name?: string | unknown;
|
||||||
|
},
|
||||||
|
DetailedHTMLProps<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>,
|
||||||
|
{}
|
||||||
|
> = styled('span')`
|
||||||
&& {
|
&& {
|
||||||
${commonStyle};
|
${commonStyle};
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ import ListItemText from '@material-ui/core/ListItemText';
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { DetailContextConsumer } from '../../pages/version/Version';
|
import { DetailContextConsumer, VersionPageConsumerProps } from '../../pages/version/Version';
|
||||||
import CopyToClipBoard from '../CopyToClipBoard';
|
import CopyToClipBoard from '../CopyToClipBoard';
|
||||||
|
|
||||||
// logos of package managers
|
// logos of package managers
|
||||||
@ -14,17 +14,17 @@ import yarn from './img/yarn.svg';
|
|||||||
import { Heading, InstallItem, PackageMangerAvatar } from './styles';
|
import { Heading, InstallItem, PackageMangerAvatar } from './styles';
|
||||||
|
|
||||||
class Install extends Component {
|
class Install extends Component {
|
||||||
public render() {
|
public render(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<DetailContextConsumer>
|
<DetailContextConsumer>
|
||||||
{(context: any) => {
|
{(context: Partial<VersionPageConsumerProps>) => {
|
||||||
return context && context.packageName && this.renderCopyCLI(context);
|
return context && context.packageName && this.renderCopyCLI(context);
|
||||||
}}
|
}}
|
||||||
</DetailContextConsumer>
|
</DetailContextConsumer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public renderCopyCLI = ({ packageName }: { packageName: string }) => {
|
public renderCopyCLI = ({ packageName = '' }: Partial<VersionPageConsumerProps>) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<List subheader={<Heading variant={'subheading'}>{'Installation'}</Heading>}>{this.renderListItems(packageName)}</List>
|
<List subheader={<Heading variant={'subheading'}>{'Installation'}</Heading>}>{this.renderListItems(packageName)}</List>
|
||||||
|
@ -6,7 +6,7 @@ interface Props {
|
|||||||
text: string;
|
text: string;
|
||||||
capitalize?: boolean;
|
capitalize?: boolean;
|
||||||
weight?: string;
|
weight?: string;
|
||||||
modifiers?: any;
|
modifiers?: null | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Wrapper = styled('div')`
|
const Wrapper = styled('div')`
|
||||||
|
@ -63,7 +63,6 @@ describe('<LoginModal />', () => {
|
|||||||
test('setCredentials - should set username and password in state', () => {
|
test('setCredentials - should set username and password in state', () => {
|
||||||
const props = {
|
const props = {
|
||||||
visibility: true,
|
visibility: true,
|
||||||
error: {},
|
|
||||||
onCancel: () => {},
|
onCancel: () => {},
|
||||||
onSubmit: () => {},
|
onSubmit: () => {},
|
||||||
};
|
};
|
||||||
@ -80,7 +79,6 @@ describe('<LoginModal />', () => {
|
|||||||
test('validateCredentials: should validate credentials', async () => {
|
test('validateCredentials: should validate credentials', async () => {
|
||||||
const props = {
|
const props = {
|
||||||
visibility: true,
|
visibility: true,
|
||||||
error: {},
|
|
||||||
onCancel: () => {},
|
onCancel: () => {},
|
||||||
onSubmit: jest.fn(),
|
onSubmit: jest.fn(),
|
||||||
};
|
};
|
||||||
@ -89,7 +87,7 @@ describe('<LoginModal />', () => {
|
|||||||
const instance = wrapper.instance();
|
const instance = wrapper.instance();
|
||||||
|
|
||||||
instance.submitCredentials = jest.fn();
|
instance.submitCredentials = jest.fn();
|
||||||
const { validateCredentials, setCredentials, submitCredentials } = instance;
|
const { handleValidateCredentials, setCredentials, submitCredentials } = instance;
|
||||||
|
|
||||||
expect(setCredentials('username', eventUsername)).toBeUndefined();
|
expect(setCredentials('username', eventUsername)).toBeUndefined();
|
||||||
expect(wrapper.state('form').username.value).toEqual('xyz');
|
expect(wrapper.state('form').username.value).toEqual('xyz');
|
||||||
@ -97,7 +95,7 @@ describe('<LoginModal />', () => {
|
|||||||
expect(setCredentials('password', eventPassword)).toBeUndefined();
|
expect(setCredentials('password', eventPassword)).toBeUndefined();
|
||||||
expect(wrapper.state('form').password.value).toEqual('1234');
|
expect(wrapper.state('form').password.value).toEqual('1234');
|
||||||
|
|
||||||
validateCredentials(event);
|
handleValidateCredentials(event);
|
||||||
expect(event.preventDefault).toHaveBeenCalled();
|
expect(event.preventDefault).toHaveBeenCalled();
|
||||||
expect(wrapper.state('form').username.pristine).toEqual(false);
|
expect(wrapper.state('form').username.pristine).toEqual(false);
|
||||||
expect(wrapper.state('form').password.pristine).toEqual(false);
|
expect(wrapper.state('form').password.pristine).toEqual(false);
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import Button from '@material-ui/core/Button';
|
import Button from '@material-ui/core/Button';
|
||||||
import DialogTitle from '@material-ui/core/DialogTitle';
|
import DialogTitle from '@material-ui/core/DialogTitle';
|
||||||
import Dialog from '@material-ui/core/Dialog';
|
import Dialog from '@material-ui/core/Dialog';
|
||||||
@ -15,22 +14,35 @@ import FormHelperText from '@material-ui/core/FormHelperText';
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import classes from './login.scss';
|
import classes from './login.scss';
|
||||||
|
|
||||||
export default class LoginModal extends Component<any, any> {
|
interface FormFields {
|
||||||
static propTypes = {
|
required: boolean;
|
||||||
visibility: PropTypes.bool,
|
pristine: boolean;
|
||||||
error: PropTypes.object,
|
helperText: string;
|
||||||
onCancel: PropTypes.func,
|
value: string;
|
||||||
onSubmit: PropTypes.func,
|
}
|
||||||
};
|
export interface FormError {
|
||||||
|
type: string;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
|
||||||
static defaultProps = {
|
interface LoginModalProps {
|
||||||
error: {},
|
visibility: boolean;
|
||||||
onCancel: () => {},
|
error?: FormError;
|
||||||
onSubmit: () => {},
|
onCancel: () => void;
|
||||||
visibility: true,
|
onSubmit: (username: string, password: string) => void;
|
||||||
};
|
}
|
||||||
|
|
||||||
constructor(props) {
|
interface LoginModalState {
|
||||||
|
form: {
|
||||||
|
username: Partial<FormFields>;
|
||||||
|
password: Partial<FormFields>;
|
||||||
|
};
|
||||||
|
error?: FormError;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class LoginModal extends Component<Partial<LoginModalProps>, LoginModalState> {
|
||||||
|
constructor(props: LoginModalProps) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
form: {
|
form: {
|
||||||
@ -51,11 +63,28 @@ export default class LoginModal extends Component<any, any> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public render(): JSX.Element {
|
||||||
|
const { visibility = true, onCancel = () => null, error } = this.props as LoginModalProps;
|
||||||
|
return (
|
||||||
|
<Dialog fullWidth={true} id={'login--form-container'} maxWidth={'xs'} onClose={onCancel} open={visibility}>
|
||||||
|
<form noValidate={true} onSubmit={this.handleValidateCredentials}>
|
||||||
|
<DialogTitle>{'Login'}</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
{error && this.renderLoginError(error)}
|
||||||
|
{this.renderNameField()}
|
||||||
|
{this.renderPasswordField()}
|
||||||
|
</DialogContent>
|
||||||
|
{this.renderActions()}
|
||||||
|
</form>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set login modal's username and password to current state
|
* set login modal's username and password to current state
|
||||||
* Required to login
|
* Required to login
|
||||||
*/
|
*/
|
||||||
setCredentials = (name, e) => {
|
public setCredentials = (name, e) => {
|
||||||
const { form } = this.state;
|
const { form } = this.state;
|
||||||
this.setState({
|
this.setState({
|
||||||
form: {
|
form: {
|
||||||
@ -69,15 +98,15 @@ export default class LoginModal extends Component<any, any> {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
setUsername = event => {
|
public handleUsernameChange = event => {
|
||||||
this.setCredentials('username', event);
|
this.setCredentials('username', event);
|
||||||
};
|
};
|
||||||
|
|
||||||
setPassword = event => {
|
public handlePasswordChange = event => {
|
||||||
this.setCredentials('password', event);
|
this.setCredentials('password', event);
|
||||||
};
|
};
|
||||||
|
|
||||||
validateCredentials = event => {
|
public handleValidateCredentials = event => {
|
||||||
const { form } = this.state;
|
const { form } = this.state;
|
||||||
// prevents default submit behavior
|
// prevents default submit behavior
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@ -89,7 +118,7 @@ export default class LoginModal extends Component<any, any> {
|
|||||||
...acc,
|
...acc,
|
||||||
...{ [key]: { ...form[key], pristine: false } },
|
...{ [key]: { ...form[key], pristine: false } },
|
||||||
}),
|
}),
|
||||||
{}
|
{ username: {}, password: {} }
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
@ -100,10 +129,14 @@ export default class LoginModal extends Component<any, any> {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
submitCredentials = async () => {
|
public submitCredentials = async () => {
|
||||||
const { form } = this.state;
|
const { form } = this.state;
|
||||||
|
const username = (form.username && form.username.value) || '';
|
||||||
|
const password = (form.password && form.password.value) || '';
|
||||||
const { onSubmit } = this.props;
|
const { onSubmit } = this.props;
|
||||||
await onSubmit(form.username.value, form.password.value);
|
if (onSubmit) {
|
||||||
|
await onSubmit(username, password);
|
||||||
|
}
|
||||||
// let's wait for API response and then set
|
// let's wait for API response and then set
|
||||||
// username and password filed to empty state
|
// username and password filed to empty state
|
||||||
this.setState({
|
this.setState({
|
||||||
@ -112,12 +145,12 @@ export default class LoginModal extends Component<any, any> {
|
|||||||
...acc,
|
...acc,
|
||||||
...{ [key]: { ...form[key], value: '', pristine: true } },
|
...{ [key]: { ...form[key], value: '', pristine: true } },
|
||||||
}),
|
}),
|
||||||
{}
|
{ username: {}, password: {} }
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
renderErrorMessage(title, description) {
|
public renderErrorMessage(title, description): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
<div>
|
<div>
|
||||||
@ -128,7 +161,7 @@ export default class LoginModal extends Component<any, any> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderMessage(title, description) {
|
public renderMessage(title, description): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<div className={classes.loginErrorMsg} id={'client-snackbar'}>
|
<div className={classes.loginErrorMsg} id={'client-snackbar'}>
|
||||||
<ErrorIcon className={classes.loginIcon} />
|
<ErrorIcon className={classes.loginIcon} />
|
||||||
@ -137,37 +170,37 @@ export default class LoginModal extends Component<any, any> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderLoginError({ type, title, description }) {
|
public renderLoginError({ type, title, description }: FormError): JSX.Element | false {
|
||||||
return type === 'error' && <SnackbarContent className={classes.loginError} message={this.renderMessage(title, description)} />;
|
return type === 'error' && <SnackbarContent className={classes.loginError} message={this.renderMessage(title, description)} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderNameField = () => {
|
public renderNameField = () => {
|
||||||
const {
|
const {
|
||||||
form: { username },
|
form: { username },
|
||||||
} = this.state;
|
} = this.state;
|
||||||
return (
|
return (
|
||||||
<FormControl error={!username.value && !username.pristine} fullWidth={true} required={username.required}>
|
<FormControl error={!username.value && !username.pristine} fullWidth={true} required={username.required}>
|
||||||
<InputLabel htmlFor={'username'}>{'Username'}</InputLabel>
|
<InputLabel htmlFor={'username'}>{'Username'}</InputLabel>
|
||||||
<Input id={'login--form-username'} onChange={this.setUsername} placeholder={'Your username'} value={username.value} />
|
<Input id={'login--form-username'} onChange={this.handleUsernameChange} placeholder={'Your username'} value={username.value} />
|
||||||
{!username.value && !username.pristine && <FormHelperText id={'username-error'}>{username.helperText}</FormHelperText>}
|
{!username.value && !username.pristine && <FormHelperText id={'username-error'}>{username.helperText}</FormHelperText>}
|
||||||
</FormControl>
|
</FormControl>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
renderPasswordField = () => {
|
public renderPasswordField = () => {
|
||||||
const {
|
const {
|
||||||
form: { password },
|
form: { password },
|
||||||
} = this.state;
|
} = this.state;
|
||||||
return (
|
return (
|
||||||
<FormControl error={!password.value && !password.pristine} fullWidth={true} required={password.required} style={{ marginTop: '8px' }}>
|
<FormControl error={!password.value && !password.pristine} fullWidth={true} required={password.required} style={{ marginTop: '8px' }}>
|
||||||
<InputLabel htmlFor={'password'}>{'Password'}</InputLabel>
|
<InputLabel htmlFor={'password'}>{'Password'}</InputLabel>
|
||||||
<Input id={'login--form-password'} onChange={this.setPassword} placeholder={'Your strong password'} type={'password'} value={password.value} />
|
<Input id={'login--form-password'} onChange={this.handlePasswordChange} placeholder={'Your strong password'} type={'password'} value={password.value} />
|
||||||
{!password.value && !password.pristine && <FormHelperText id={'password-error'}>{password.helperText}</FormHelperText>}
|
{!password.value && !password.pristine && <FormHelperText id={'password-error'}>{password.helperText}</FormHelperText>}
|
||||||
</FormControl>
|
</FormControl>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
renderActions = () => {
|
public renderActions = () => {
|
||||||
const {
|
const {
|
||||||
form: { username, password },
|
form: { username, password },
|
||||||
} = this.state;
|
} = this.state;
|
||||||
@ -183,21 +216,4 @@ export default class LoginModal extends Component<any, any> {
|
|||||||
</DialogActions>
|
</DialogActions>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
|
||||||
const { visibility, onCancel, error } = this.props;
|
|
||||||
return (
|
|
||||||
<Dialog fullWidth={true} id={'login--form-container'} maxWidth={'xs'} onClose={onCancel} open={visibility}>
|
|
||||||
<form noValidate={true} onSubmit={this.validateCredentials}>
|
|
||||||
<DialogTitle>{'Login'}</DialogTitle>
|
|
||||||
<DialogContent>
|
|
||||||
{this.renderLoginError(error)}
|
|
||||||
{this.renderNameField()}
|
|
||||||
{this.renderPasswordField()}
|
|
||||||
</DialogContent>
|
|
||||||
{this.renderActions()}
|
|
||||||
</form>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -6,21 +6,22 @@ import { RouteComponentProps, withRouter } from 'react-router-dom';
|
|||||||
|
|
||||||
import PackageImg from './img/package.svg';
|
import PackageImg from './img/package.svg';
|
||||||
import { Card, EmptyPackage, Heading, Inner, List, Wrapper } from './styles';
|
import { Card, EmptyPackage, Heading, Inner, List, Wrapper } from './styles';
|
||||||
|
import { Breakpoint } from '@material-ui/core/styles/createBreakpoints';
|
||||||
|
|
||||||
export const NOT_FOUND_TEXT = "Sorry, we couldn't find it...";
|
export const NOT_FOUND_TEXT = "Sorry, we couldn't find it...";
|
||||||
|
|
||||||
export type NotFoundProps = RouteComponentProps & { width: any; history: any };
|
export type NotFoundProps = RouteComponentProps & { width: Breakpoint; history };
|
||||||
|
|
||||||
const NotFound: React.FC<NotFoundProps> = ({ history, width }) => {
|
const NotFound: React.FC<NotFoundProps> = ({ history, width }) => {
|
||||||
const handleGoTo = (to: string) => () => {
|
const handleGoTo = (to: string): (() => void | undefined) => () => {
|
||||||
history.push(to);
|
history.push(to);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleGoBack = () => () => {
|
const handleGoBack = (): ((e: React.MouseEvent<HTMLElement, MouseEvent>) => void | undefined) => () => {
|
||||||
history.goBack();
|
history.goBack();
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderList = () => (
|
const renderList = (): JSX.Element => (
|
||||||
<List>
|
<List>
|
||||||
<ListItem button={true} divider={true} onClick={handleGoTo('/')}>
|
<ListItem button={true} divider={true} onClick={handleGoTo('/')}>
|
||||||
{'Home'}
|
{'Home'}
|
||||||
@ -31,7 +32,7 @@ const NotFound: React.FC<NotFoundProps> = ({ history, width }) => {
|
|||||||
</List>
|
</List>
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderSubTitle = () => (
|
const renderSubTitle = (): JSX.Element => (
|
||||||
<Typography variant="subtitle1">
|
<Typography variant="subtitle1">
|
||||||
<div>{"The page you're looking for doesn't exist."}</div>
|
<div>{"The page you're looking for doesn't exist."}</div>
|
||||||
<div>{'Perhaps these links will help find what you are looking for:'}</div>
|
<div>{'Perhaps these links will help find what you are looking for:'}</div>
|
||||||
|
@ -8,7 +8,7 @@ import { WrapperLink, Description, OverviewItem } from './styles';
|
|||||||
* Generates one month back date from current time
|
* Generates one month back date from current time
|
||||||
* @return {object} date object
|
* @return {object} date object
|
||||||
*/
|
*/
|
||||||
const dateOneMonthAgo = () => new Date(1544377770747);
|
const dateOneMonthAgo = (): Date => new Date(1544377770747);
|
||||||
|
|
||||||
describe('<Package /> component', () => {
|
describe('<Package /> component', () => {
|
||||||
test.skip('should load the component', () => {
|
test.skip('should load the component', () => {
|
||||||
|
@ -23,10 +23,10 @@ interface Dist {
|
|||||||
unpackedSize: number;
|
unpackedSize: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
export interface PackageInterface {
|
||||||
name: string;
|
name: string;
|
||||||
version: string;
|
version: string;
|
||||||
time: string;
|
time?: number | string;
|
||||||
author: Author;
|
author: Author;
|
||||||
description?: string;
|
description?: string;
|
||||||
keywords?: string[];
|
keywords?: string[];
|
||||||
@ -35,6 +35,7 @@ interface Props {
|
|||||||
bugs?: Bugs;
|
bugs?: Bugs;
|
||||||
dist?: Dist;
|
dist?: Dist;
|
||||||
}
|
}
|
||||||
|
// interface Props {} & PackageInterface;
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Author,
|
Author,
|
||||||
@ -56,7 +57,7 @@ import {
|
|||||||
} from './styles';
|
} from './styles';
|
||||||
import { isURL } from '../../utils/url';
|
import { isURL } from '../../utils/url';
|
||||||
|
|
||||||
const Package: React.FC<Props> = ({
|
const Package: React.FC<PackageInterface> = ({
|
||||||
author: { name: authorName, avatar: authorAvatar },
|
author: { name: authorName, avatar: authorAvatar },
|
||||||
bugs,
|
bugs,
|
||||||
description,
|
description,
|
||||||
|
@ -1,25 +1,21 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment, ReactElement } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
import Divider from '@material-ui/core/Divider';
|
import Divider from '@material-ui/core/Divider';
|
||||||
|
|
||||||
import Package from '../Package';
|
import Package from '../Package';
|
||||||
import Help from '../Help';
|
import Help from '../Help';
|
||||||
import { formatLicense } from '../../utils/package';
|
import { formatLicense } from '../../utils/package';
|
||||||
|
import { PackageInterface } from '../Package/Package';
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import classes from './packageList.scss';
|
import classes from './packageList.scss';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
packages: any;
|
packages: PackageInterface[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class PackageList extends React.Component<Props, {}> {
|
export default class PackageList extends React.Component<Props, {}> {
|
||||||
static propTypes = {
|
public render(): ReactElement<HTMLElement> {
|
||||||
packages: PropTypes.array,
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
return (
|
||||||
<div className={'package-list-items'}>
|
<div className={'package-list-items'}>
|
||||||
<div className={classes.pkgContainer}>{this.hasPackages() ? this.renderPackages() : <Help />}</div>
|
<div className={classes.pkgContainer}>{this.hasPackages() ? this.renderPackages() : <Help />}</div>
|
||||||
@ -27,20 +23,19 @@ export default class PackageList extends React.Component<Props, {}> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
hasPackages() {
|
private hasPackages(): boolean {
|
||||||
const { packages } = this.props;
|
const { packages } = this.props;
|
||||||
return packages.length > 0;
|
return packages.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderPackages = () => {
|
private renderPackages = () => {
|
||||||
return <>{this.renderList()}</>;
|
return <>{this.renderList()}</>;
|
||||||
};
|
};
|
||||||
|
|
||||||
renderList = () => {
|
private renderList = () => {
|
||||||
const { packages } = this.props;
|
const { packages } = this.props;
|
||||||
return packages.map((pkg, i) => {
|
return packages.map((pkg, i) => {
|
||||||
const { name, version, description, time, keywords, dist, homepage, bugs } = pkg;
|
const { name, version, description, time, keywords, dist, homepage, bugs, author } = pkg;
|
||||||
const author = pkg.author;
|
|
||||||
// TODO: move format license to API side.
|
// TODO: move format license to API side.
|
||||||
const license = formatLicense(pkg.license);
|
const license = formatLicense(pkg.license);
|
||||||
return (
|
return (
|
||||||
|
@ -11,7 +11,7 @@ import { getCLISetRegistry, getCLIChangePassword, getCLISetConfigRegistry } from
|
|||||||
import { NODE_MANAGER } from '../../utils/constants';
|
import { NODE_MANAGER } from '../../utils/constants';
|
||||||
|
|
||||||
/* eslint react/prop-types:0 */
|
/* eslint react/prop-types:0 */
|
||||||
function TabContainer({ children }) {
|
function TabContainer({ children }): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<CommandContainer>
|
<CommandContainer>
|
||||||
<Typography component="div" style={{ padding: 0, minHeight: 170 }}>
|
<Typography component="div" style={{ padding: 0, minHeight: 170 }}>
|
||||||
@ -22,15 +22,20 @@ function TabContainer({ children }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class RegistryInfoContent extends Component<Props, State> {
|
class RegistryInfoContent extends Component<Props, State> {
|
||||||
state = {
|
public state = {
|
||||||
tabPosition: 0,
|
tabPosition: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
public render(): JSX.Element {
|
||||||
return <div>{this.renderTabs()}</div>;
|
return <div>{this.renderTabs()}</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderTabs() {
|
private handleChange = (event: React.ChangeEvent<{}>, tabPosition: number) => {
|
||||||
|
event.preventDefault();
|
||||||
|
this.setState({ tabPosition });
|
||||||
|
};
|
||||||
|
|
||||||
|
private renderTabs(): JSX.Element {
|
||||||
const { scope, registryUrl } = this.props;
|
const { scope, registryUrl } = this.props;
|
||||||
const { tabPosition } = this.state;
|
const { tabPosition } = this.state;
|
||||||
|
|
||||||
@ -48,7 +53,7 @@ class RegistryInfoContent extends Component<Props, State> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderNpmTab(scope: string, registryUrl: string) {
|
private renderNpmTab(scope: string, registryUrl: string): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<CopyToClipBoard text={getCLISetConfigRegistry(`${NODE_MANAGER.npm} set`, scope, registryUrl)} />
|
<CopyToClipBoard text={getCLISetConfigRegistry(`${NODE_MANAGER.npm} set`, scope, registryUrl)} />
|
||||||
@ -58,7 +63,7 @@ class RegistryInfoContent extends Component<Props, State> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderPNpmTab(scope: string, registryUrl: string) {
|
private renderPNpmTab(scope: string, registryUrl: string): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<CopyToClipBoard text={getCLISetConfigRegistry(`${NODE_MANAGER.pnpm} set`, scope, registryUrl)} />
|
<CopyToClipBoard text={getCLISetConfigRegistry(`${NODE_MANAGER.pnpm} set`, scope, registryUrl)} />
|
||||||
@ -68,18 +73,13 @@ class RegistryInfoContent extends Component<Props, State> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderYarnTab(scope: string, registryUrl: string) {
|
private renderYarnTab(scope: string, registryUrl: string): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<CopyToClipBoard text={getCLISetConfigRegistry(`${NODE_MANAGER.yarn} config set`, scope, registryUrl)} />
|
<CopyToClipBoard text={getCLISetConfigRegistry(`${NODE_MANAGER.yarn} config set`, scope, registryUrl)} />
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleChange = (event: any, tabPosition: number) => {
|
|
||||||
event.preventDefault();
|
|
||||||
this.setState({ tabPosition });
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default RegistryInfoContent;
|
export default RegistryInfoContent;
|
||||||
|
@ -8,7 +8,7 @@ import { Props } from './types';
|
|||||||
|
|
||||||
const LABEL = 'CLOSE';
|
const LABEL = 'CLOSE';
|
||||||
|
|
||||||
const RegistryInfoDialog: React.FC<Props> = ({ open = false, children, onClose }): any => (
|
const RegistryInfoDialog: React.FC<Props> = ({ open = false, children, onClose }) => (
|
||||||
<Dialog id="registryInfo--dialog-container" onClose={onClose} open={open}>
|
<Dialog id="registryInfo--dialog-container" onClose={onClose} open={open}>
|
||||||
<Title disableTypography={true}>{'Register Info'}</Title>
|
<Title disableTypography={true}>{'Register Info'}</Title>
|
||||||
<Content>{children}</Content>
|
<Content>{children}</Content>
|
||||||
|
@ -13,7 +13,7 @@ import { Heading, GithubLink, RepositoryListItem } from './styles';
|
|||||||
import git from './img/git.png';
|
import git from './img/git.png';
|
||||||
import { isURL } from '../../utils/url';
|
import { isURL } from '../../utils/url';
|
||||||
|
|
||||||
class Repository extends Component<any, any> {
|
class Repository extends Component {
|
||||||
public render(): ReactElement<HTMLElement> {
|
public render(): ReactElement<HTMLElement> {
|
||||||
return (
|
return (
|
||||||
<DetailContextConsumer>
|
<DetailContextConsumer>
|
||||||
@ -33,12 +33,7 @@ class Repository extends Component<any, any> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private renderRepository = packageMeta => {
|
private renderRepository = packageMeta => {
|
||||||
const {
|
const { repository: { url = null } = {} } = packageMeta.latest;
|
||||||
repository: {
|
|
||||||
// @ts-ignore
|
|
||||||
url,
|
|
||||||
} = {},
|
|
||||||
} = packageMeta.latest;
|
|
||||||
|
|
||||||
if (!url || isURL(url) === false) {
|
if (!url || isURL(url) === false) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -154,7 +154,7 @@ describe('<Search /> component test', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
jest.resetModules();
|
jest.resetModules();
|
||||||
jest.doMock('lodash/debounce', () => {
|
jest.doMock('lodash/debounce', () => {
|
||||||
return function debounceMock(fn, delay) {
|
return function debounceMock(fn) {
|
||||||
return fn;
|
return fn;
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -11,11 +11,15 @@ import colors from '../../utils/styles/colors';
|
|||||||
|
|
||||||
export interface State {
|
export interface State {
|
||||||
search: string;
|
search: string;
|
||||||
suggestions: any[];
|
suggestions: unknown[];
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
loaded: boolean;
|
loaded: boolean;
|
||||||
error: boolean;
|
error: boolean;
|
||||||
}
|
}
|
||||||
|
interface AbortControllerInterface {
|
||||||
|
signal: () => void;
|
||||||
|
abort: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
export type cancelAllSearchRequests = () => void;
|
export type cancelAllSearchRequests = () => void;
|
||||||
export type handlePackagesClearRequested = () => void;
|
export type handlePackagesClearRequested = () => void;
|
||||||
@ -31,8 +35,6 @@ const CONSTANTS = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export class Search extends Component<RouteComponentProps<{}>, State> {
|
export class Search extends Component<RouteComponentProps<{}>, State> {
|
||||||
private requestList: any[];
|
|
||||||
|
|
||||||
constructor(props: RouteComponentProps<{}>) {
|
constructor(props: RouteComponentProps<{}>) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
@ -96,7 +98,10 @@ export class Search extends Component<RouteComponentProps<{}>, State> {
|
|||||||
/**
|
/**
|
||||||
* When an user select any package by clicking or pressing return key.
|
* When an user select any package by clicking or pressing return key.
|
||||||
*/
|
*/
|
||||||
private handleClickSearch: handleClickSearch = (event, { suggestionValue, method }: any) => {
|
private handleClickSearch = (
|
||||||
|
event: React.KeyboardEvent<HTMLInputElement>,
|
||||||
|
{ suggestionValue, method }: { suggestionValue: string[]; method: string }
|
||||||
|
): void | undefined => {
|
||||||
const { history } = this.props;
|
const { history } = this.props;
|
||||||
// stops event bubbling
|
// stops event bubbling
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
@ -163,7 +168,9 @@ export class Search extends Component<RouteComponentProps<{}>, State> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getAdorment(): ReactElement<HTMLElement> {
|
private requestList: AbortControllerInterface[];
|
||||||
|
|
||||||
|
public getAdorment(): JSX.Element {
|
||||||
return (
|
return (
|
||||||
<InputAdornment position={'start'} style={{ color: colors.white }}>
|
<InputAdornment position={'start'} style={{ color: colors.white }}>
|
||||||
<IconSearch />
|
<IconSearch />
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import CircularProgress from '@material-ui/core/CircularProgress';
|
import CircularProgress from '@material-ui/core/CircularProgress';
|
||||||
import styled, { css, Themed } from 'react-emotion';
|
import styled, { css } from 'react-emotion';
|
||||||
|
|
||||||
import colors from '../../utils/styles/colors';
|
import colors from '../../utils/styles/colors';
|
||||||
|
|
||||||
@ -8,7 +8,7 @@ export const Wrapper = styled('div')`
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
${(props): Themed<any, object> =>
|
${props =>
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
props.centered &&
|
props.centered &&
|
||||||
css`
|
css`
|
||||||
|
@ -8,7 +8,7 @@ import { formatDateDistance } from '../../utils/package';
|
|||||||
|
|
||||||
import { Heading, Spacer, ListItemText } from './styles';
|
import { Heading, Spacer, ListItemText } from './styles';
|
||||||
|
|
||||||
class UpLinks extends React.PureComponent<any> {
|
class UpLinks extends React.PureComponent<{}> {
|
||||||
public render(): ReactElement<HTMLElement> {
|
public render(): ReactElement<HTMLElement> {
|
||||||
return (
|
return (
|
||||||
<DetailContextConsumer>
|
<DetailContextConsumer>
|
||||||
|
@ -8,7 +8,7 @@ import { DIST_TAGS } from '../../../lib/constants';
|
|||||||
|
|
||||||
const NOT_AVAILABLE = 'Not available';
|
const NOT_AVAILABLE = 'Not available';
|
||||||
|
|
||||||
class Versions extends React.PureComponent<any> {
|
class Versions extends React.PureComponent {
|
||||||
public render(): ReactElement<HTMLDivElement> {
|
public render(): ReactElement<HTMLDivElement> {
|
||||||
return (
|
return (
|
||||||
<DetailContextConsumer>
|
<DetailContextConsumer>
|
||||||
@ -19,7 +19,7 @@ class Versions extends React.PureComponent<any> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public renderPackageList = (packages: any, isVersion: boolean = false, timeMap: Record<string, any> = {}): ReactElement<HTMLDivElement> => {
|
public renderPackageList = (packages: {}, isVersion: boolean = false, timeMap: Record<string, {}> = {}): ReactElement<HTMLDivElement> => {
|
||||||
return (
|
return (
|
||||||
<List>
|
<List>
|
||||||
{Object.keys(packages)
|
{Object.keys(packages)
|
||||||
|
@ -8,7 +8,7 @@ import App from './App';
|
|||||||
|
|
||||||
const rootNode = document.getElementById('root');
|
const rootNode = document.getElementById('root');
|
||||||
|
|
||||||
const renderApp = Component => {
|
const renderApp = (Component): void => {
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<AppContainer>
|
<AppContainer>
|
||||||
<Component />
|
<Component />
|
||||||
|
@ -4,7 +4,7 @@ import PackageList from '../../components/PackageList';
|
|||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
isUserLoggedIn: boolean;
|
isUserLoggedIn: boolean;
|
||||||
packages: any[];
|
packages: [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const Home: React.FC<Props> = ({ packages }) => (
|
const Home: React.FC<Props> = ({ packages }) => (
|
||||||
|
@ -3,31 +3,44 @@ import Grid from '@material-ui/core/Grid';
|
|||||||
import Loading from '../../components/Loading/Loading';
|
import Loading from '../../components/Loading/Loading';
|
||||||
import DetailContainer from '../../components/DetailContainer/DetailContainer';
|
import DetailContainer from '../../components/DetailContainer/DetailContainer';
|
||||||
import DetailSidebar from '../../components/DetailSidebar/DetailSidebar';
|
import DetailSidebar from '../../components/DetailSidebar/DetailSidebar';
|
||||||
import { callDetailPage, DetailPage } from '../../utils/calls';
|
import { callDetailPage } from '../../utils/calls';
|
||||||
import { getRouterPackageName } from '../../utils/package';
|
import { getRouterPackageName } from '../../utils/package';
|
||||||
import NotFound from '../../components/NotFound';
|
import NotFound from '../../components/NotFound';
|
||||||
|
import { PackageMetaInterface } from '../../../types/packageMeta';
|
||||||
|
|
||||||
export interface DetailContextProps {
|
export interface DetailContextProps {
|
||||||
packageMeta: any;
|
packageMeta: PackageMetaInterface;
|
||||||
readMe: any;
|
readMe: string;
|
||||||
packageName: string;
|
packageName: string;
|
||||||
enableLoading: () => void;
|
enableLoading: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DetailContext = React.createContext<DetailContextProps | null>(null);
|
export const DetailContext = React.createContext<Partial<DetailContextProps>>({});
|
||||||
|
|
||||||
export interface VersionPageConsumerProps {
|
export interface VersionPageConsumerProps {
|
||||||
packageMeta: any;
|
packageMeta: PackageMetaInterface;
|
||||||
readMe: any;
|
readMe: string;
|
||||||
packageName: any;
|
packageName: string;
|
||||||
enableLoading: any;
|
enableLoading: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DetailContextProvider: Provider<VersionPageConsumerProps | null> = DetailContext.Provider;
|
export const DetailContextProvider: Provider<Partial<VersionPageConsumerProps>> = DetailContext.Provider;
|
||||||
export const DetailContextConsumer: Consumer<VersionPageConsumerProps | null> = DetailContext.Consumer;
|
export const DetailContextConsumer: Consumer<Partial<VersionPageConsumerProps>> = DetailContext.Consumer;
|
||||||
|
|
||||||
class VersionPage extends Component<any, any> {
|
interface PropsInterface {
|
||||||
constructor(props: any) {
|
match: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface StateInterface {
|
||||||
|
readMe: string;
|
||||||
|
packageName: string;
|
||||||
|
packageMeta: PackageMetaInterface | null;
|
||||||
|
isLoading: boolean;
|
||||||
|
notFound: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
class VersionPage extends Component<PropsInterface, Partial<StateInterface>> {
|
||||||
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
@ -39,7 +52,7 @@ class VersionPage extends Component<any, any> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static getDerivedStateFromProps(nextProps: any, prevState: any): any {
|
public static getDerivedStateFromProps(nextProps, prevState): { packageName?: string; isLoading: boolean; notFound?: boolean } | null {
|
||||||
const { match } = nextProps;
|
const { match } = nextProps;
|
||||||
const packageName = getRouterPackageName(match);
|
const packageName = getRouterPackageName(match);
|
||||||
|
|
||||||
@ -65,10 +78,10 @@ class VersionPage extends Component<any, any> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* eslint no-unused-vars: 0 */
|
/* eslint no-unused-vars: 0 */
|
||||||
public async componentDidUpdate(nextProps: any, prevState: any): Promise<void> {
|
public async componentDidUpdate(nextProps, prevState: StateInterface): Promise<void> {
|
||||||
const { packageName } = this.state;
|
const { packageName } = this.state;
|
||||||
if (packageName !== prevState.packageName) {
|
if (packageName !== prevState.packageName) {
|
||||||
const { readMe, packageMeta } = await callDetailPage(packageName);
|
const { readMe, packageMeta } = (await callDetailPage(packageName)) as Partial<StateInterface>;
|
||||||
this.setState({
|
this.setState({
|
||||||
readMe,
|
readMe,
|
||||||
packageMeta,
|
packageMeta,
|
||||||
@ -112,7 +125,7 @@ class VersionPage extends Component<any, any> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { readMe, packageMeta } = await callDetailPage(packageName);
|
const { readMe, packageMeta } = (await callDetailPage(packageName)) as Partial<StateInterface>;
|
||||||
this.setState({
|
this.setState({
|
||||||
readMe,
|
readMe,
|
||||||
packageMeta,
|
packageMeta,
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import React, { Component, ReactElement } from 'react';
|
import React, { Component, ReactElement } from 'react';
|
||||||
import { Router, Route, Switch } from 'react-router-dom';
|
import { Router, Route, Switch } from 'react-router-dom';
|
||||||
import { AppContextConsumer } from './App/App';
|
import { AppContextConsumer, AppStateInterface } from './App/App';
|
||||||
|
|
||||||
import { asyncComponent } from './utils/asyncComponent';
|
import { asyncComponent } from './utils/asyncComponent';
|
||||||
import history from './history';
|
import history from './history';
|
||||||
@ -12,7 +12,12 @@ const NotFound = asyncComponent(() => import('./components/NotFound'));
|
|||||||
const VersionPackage = asyncComponent(() => import('./pages/version/Version'));
|
const VersionPackage = asyncComponent(() => import('./pages/version/Version'));
|
||||||
const HomePage = asyncComponent(() => import('./pages/home'));
|
const HomePage = asyncComponent(() => import('./pages/home'));
|
||||||
|
|
||||||
class RouterApp extends Component<any, any> {
|
interface RouterAppProps {
|
||||||
|
onLogout: () => void;
|
||||||
|
onToggleLoginModal: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
class RouterApp extends Component<RouterAppProps> {
|
||||||
public render(): ReactElement<HTMLDivElement> {
|
public render(): ReactElement<HTMLDivElement> {
|
||||||
return (
|
return (
|
||||||
<Router history={history}>
|
<Router history={history}>
|
||||||
@ -34,8 +39,8 @@ class RouterApp extends Component<any, any> {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<AppContextConsumer>
|
<AppContextConsumer>
|
||||||
{function renderConsumerVersionPage({ logoUrl, scope, user }: any) {
|
{function renderConsumerVersionPage({ logoUrl, scope = '', user }: Partial<AppStateInterface>) {
|
||||||
return <Header logo={logoUrl} onLogout={onLogout} onToggleLoginModal={onToggleLoginModal} scope={scope} username={user.username} />;
|
return <Header logo={logoUrl} onLogout={onLogout} onToggleLoginModal={onToggleLoginModal} scope={scope} username={user && user.username} />;
|
||||||
}}
|
}}
|
||||||
</AppContextConsumer>
|
</AppContextConsumer>
|
||||||
);
|
);
|
||||||
@ -44,7 +49,7 @@ class RouterApp extends Component<any, any> {
|
|||||||
public renderHomePage = (): ReactElement<HTMLDivElement> => {
|
public renderHomePage = (): ReactElement<HTMLDivElement> => {
|
||||||
return (
|
return (
|
||||||
<AppContextConsumer>
|
<AppContextConsumer>
|
||||||
{function renderConsumerVersionPage({ isUserLoggedIn, packages }: any) {
|
{function renderConsumerVersionPage({ isUserLoggedIn, packages }: Partial<AppStateInterface>) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
return <HomePage isUserLoggedIn={isUserLoggedIn} packages={packages} />;
|
return <HomePage isUserLoggedIn={isUserLoggedIn} packages={packages} />;
|
||||||
}}
|
}}
|
||||||
@ -52,10 +57,10 @@ class RouterApp extends Component<any, any> {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
public renderVersionPage = (routerProps: any): ReactElement<HTMLDivElement> => {
|
public renderVersionPage = (routerProps): ReactElement<HTMLDivElement> => {
|
||||||
return (
|
return (
|
||||||
<AppContextConsumer>
|
<AppContextConsumer>
|
||||||
{function renderConsumerVersionPage({ isUserLoggedIn }: any) {
|
{function renderConsumerVersionPage({ isUserLoggedIn }: Partial<AppStateInterface>) {
|
||||||
return <VersionPackage {...routerProps} isUserLoggedIn={isUserLoggedIn} />;
|
return <VersionPackage {...routerProps} isUserLoggedIn={isUserLoggedIn} />;
|
||||||
}}
|
}}
|
||||||
</AppContextConsumer>
|
</AppContextConsumer>
|
||||||
|
@ -6,9 +6,9 @@ import '../../types';
|
|||||||
* @param {object} response
|
* @param {object} response
|
||||||
* @returns {promise}
|
* @returns {promise}
|
||||||
*/
|
*/
|
||||||
function handleResponseType(response): Promise<any> {
|
function handleResponseType(response: Response): Promise<[boolean, Blob | string]> | Promise<void> {
|
||||||
if (response.headers) {
|
if (response.headers) {
|
||||||
const contentType = response.headers.get('Content-Type');
|
const contentType = response.headers.get('Content-Type') as string;
|
||||||
if (contentType.includes('application/pdf')) {
|
if (contentType.includes('application/pdf')) {
|
||||||
return Promise.all([response.ok, response.blob()]);
|
return Promise.all([response.ok, response.blob()]);
|
||||||
}
|
}
|
||||||
@ -25,16 +25,16 @@ function handleResponseType(response): Promise<any> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class API {
|
class API {
|
||||||
public request(url: string, method = 'GET', options: any = {}): Promise<any> {
|
public request<T>(url: string, method = 'GET', options?: RequestInit): Promise<T> {
|
||||||
if (!window.VERDACCIO_API_URL) {
|
if (!window.VERDACCIO_API_URL) {
|
||||||
throw new Error('VERDACCIO_API_URL is not defined!');
|
throw new Error('VERDACCIO_API_URL is not defined!');
|
||||||
}
|
}
|
||||||
|
|
||||||
const token = storage.getItem('token');
|
const token = storage.getItem('token');
|
||||||
if (token) {
|
const headers = new Headers(options && options.headers);
|
||||||
if (!options.headers) options.headers = {};
|
if (token && options && options.headers) {
|
||||||
|
headers.set('Authorization', `Bearer ${token}`);
|
||||||
options.headers.authorization = `Bearer ${token}`;
|
options.headers = Object.assign(options.headers, headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!['http://', 'https://', '//'].some(prefix => url.startsWith(prefix))) {
|
if (!['http://', 'https://', '//'].some(prefix => url.startsWith(prefix))) {
|
||||||
@ -42,7 +42,7 @@ class API {
|
|||||||
url = window.VERDACCIO_API_URL + url;
|
url = window.VERDACCIO_API_URL + url;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Promise<any>((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
fetch(url, {
|
fetch(url, {
|
||||||
method,
|
method,
|
||||||
credentials: 'same-origin',
|
credentials: 'same-origin',
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import React from 'react';
|
import React, { ComponentClass } from 'react';
|
||||||
|
|
||||||
export function asyncComponent(getComponent) {
|
export function asyncComponent(getComponent): ComponentClass {
|
||||||
return class AsyncComponent extends React.Component {
|
return class AsyncComponent extends React.Component {
|
||||||
static Component = null;
|
public static Component = null;
|
||||||
state = { Component: AsyncComponent.Component };
|
public state = { Component: AsyncComponent.Component };
|
||||||
|
|
||||||
componentDidMount() {
|
public componentDidMount(): void {
|
||||||
const { Component } = this.state;
|
const { Component } = this.state;
|
||||||
if (!Component) {
|
if (!Component) {
|
||||||
getComponent()
|
getComponent()
|
||||||
@ -19,7 +19,8 @@ export function asyncComponent(getComponent) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
render() {
|
|
||||||
|
public render(): JSX.Element | null {
|
||||||
const { Component } = this.state;
|
const { Component } = this.state;
|
||||||
if (Component) {
|
if (Component) {
|
||||||
// eslint-disable-next-line verdaccio/jsx-spread
|
// eslint-disable-next-line verdaccio/jsx-spread
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import API from './api';
|
import API from './api';
|
||||||
|
import { PackageMetaInterface } from 'types/packageMeta';
|
||||||
|
|
||||||
export interface DetailPage {
|
export interface DetailPage {
|
||||||
readMe: any;
|
readMe: string | {};
|
||||||
packageMeta: any;
|
packageMeta: PackageMetaInterface | {};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function callDetailPage(packageName): Promise<DetailPage> {
|
export async function callDetailPage(packageName): Promise<DetailPage> {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { SyntheticEvent } from 'react';
|
import { SyntheticEvent } from 'react';
|
||||||
|
|
||||||
export const copyToClipBoardUtility = (str: string): any => (event: SyntheticEvent<HTMLElement>): void => {
|
export const copyToClipBoardUtility = (str: string): ((e: SyntheticEvent<HTMLElement>) => void) => (event: SyntheticEvent<HTMLElement>): void => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
const node = document.createElement('div');
|
const node = document.createElement('div');
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
export default function fileSizeSI(a?: any, b?: any, c?: any, d?: any, e?: any) {
|
export default function fileSizeSI(a: number, b?: typeof Math, c?: (p: number) => number, d?: number, e?: number): string {
|
||||||
return ((b = Math), (c = b.log), (d = 1e3), (e = (c(a) / c(d)) | 0), a / b.pow(d, e)).toFixed(2) + ' ' + (e ? 'kMGTPEZY'[--e] + 'B' : 'Bytes');
|
return ((b = Math), (c = b.log), (d = 1e3), (e = (c(a) / c(d)) | 0), a / b.pow(d, e)).toFixed(2) + ' ' + (e ? 'kMGTPEZY'[--e] + 'B' : 'Bytes');
|
||||||
}
|
}
|
||||||
|
@ -5,30 +5,34 @@ import { Base64 } from 'js-base64';
|
|||||||
import API from './api';
|
import API from './api';
|
||||||
import { HEADERS } from '../../lib/constants';
|
import { HEADERS } from '../../lib/constants';
|
||||||
|
|
||||||
export function isTokenExpire(token?: any) {
|
interface PayloadInterface {
|
||||||
|
exp: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isTokenExpire(token?: string): boolean {
|
||||||
if (!isString(token)) {
|
if (!isString(token)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let [, payload]: any = token.split('.');
|
const [, payload] = token.split('.');
|
||||||
|
|
||||||
if (!payload) {
|
if (!payload) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let exp: number;
|
||||||
try {
|
try {
|
||||||
payload = JSON.parse(Base64.decode(payload));
|
exp = JSON.parse(Base64.decode(payload)).exp;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// eslint-disable-next-line
|
console.error('Invalid token:', error, token);
|
||||||
console.error('Invalid token:', error, token);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!payload.exp || !isNumber(payload.exp)) {
|
if (!exp || !isNumber(exp)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// Report as expire before (real expire time - 30s)
|
// Report as expire before (real expire time - 30s)
|
||||||
const jsTimestamp = payload.exp * 1000 - 30000;
|
const jsTimestamp = exp * 1000 - 30000;
|
||||||
const expired = Date.now() >= jsTimestamp;
|
const expired = Date.now() >= jsTimestamp;
|
||||||
|
|
||||||
return expired;
|
return expired;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import parseXSS from 'xss';
|
import parseXSS from 'xss';
|
||||||
|
|
||||||
export function preventXSS(text: string) {
|
export function preventXSS(text: string): string {
|
||||||
const encodedText = parseXSS.filterXSS(text);
|
const encodedText = parseXSS.filterXSS(text);
|
||||||
|
|
||||||
return encodedText;
|
return encodedText;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* CSS to represent truncated text with an ellipsis.
|
* CSS to represent truncated text with an ellipsis.
|
||||||
*/
|
*/
|
||||||
export function ellipsis(width: string | number) {
|
export function ellipsis(width: string | number): {} {
|
||||||
return {
|
return {
|
||||||
display: 'inline-block',
|
display: 'inline-block',
|
||||||
maxWidth: width,
|
maxWidth: width,
|
||||||
@ -24,7 +24,7 @@ interface SpacingShortHand<type> {
|
|||||||
|
|
||||||
const positionMap = ['Top', 'Right', 'Bottom', 'Left'];
|
const positionMap = ['Top', 'Right', 'Bottom', 'Left'];
|
||||||
|
|
||||||
export function spacing(property: 'padding' | 'margin', ...values: SpacingShortHand<number | string>[]) {
|
export function spacing(property: 'padding' | 'margin', ...values: SpacingShortHand<number | string>[]): {} {
|
||||||
const [firstValue = 0, secondValue = 0, thirdValue = 0, fourthValue = 0] = values;
|
const [firstValue = 0, secondValue = 0, thirdValue = 0, fourthValue = 0] = values;
|
||||||
const valuesWithDefaults = [firstValue, secondValue, thirdValue, fourthValue];
|
const valuesWithDefaults = [firstValue, secondValue, thirdValue, fourthValue];
|
||||||
let styles = {};
|
let styles = {};
|
||||||
|
7
types/custom.d.ts
vendored
7
types/custom.d.ts
vendored
@ -1,10 +1,7 @@
|
|||||||
// https://stackoverflow.com/questions/44717164/unable-to-import-svg-files-in-typescript
|
// https://stackoverflow.com/questions/44717164/unable-to-import-svg-files-in-typescript
|
||||||
declare module '*.svg' {
|
declare module '*.svg' {
|
||||||
const content: any;
|
const content: string;
|
||||||
export default content;
|
export default content;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module '*.png' {
|
declare module '*.png';
|
||||||
const content: any;
|
|
||||||
export default content;
|
|
||||||
}
|
|
||||||
|
11
types/packageMeta.ts
Normal file
11
types/packageMeta.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
export interface PackageMetaInterface {
|
||||||
|
latest: {
|
||||||
|
name: string;
|
||||||
|
dist: {
|
||||||
|
fileCount: number;
|
||||||
|
unpackedSize: number;
|
||||||
|
};
|
||||||
|
license: string;
|
||||||
|
};
|
||||||
|
_uplinks: {};
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user