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
|
||||
*/
|
||||
class API {
|
||||
request() {
|
||||
return register.call(null, ...arguments);
|
||||
public request(...rest) {
|
||||
return register.call(null, ...rest);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,6 @@
|
||||
* Mock response for logo api
|
||||
* @returns {promise}
|
||||
*/
|
||||
export default function() {
|
||||
export default function<T>(): Promise<T> {
|
||||
return Promise.resolve('http://localhost/-/static/logo.png');
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ import { generateTokenWithTimeRange } from '../../jest/unit/components/__mocks__
|
||||
|
||||
jest.mock('../utils/storage', () => {
|
||||
class LocalStorageMock {
|
||||
store: object;
|
||||
private store: object;
|
||||
public constructor() {
|
||||
this.store = {};
|
||||
}
|
||||
@ -43,7 +43,7 @@ describe('App', () => {
|
||||
expect(wrapper.state().showLoginModal).toBeFalsy();
|
||||
handleToggleLoginModal();
|
||||
expect(wrapper.state('showLoginModal')).toBeTruthy();
|
||||
expect(wrapper.state('error')).toEqual({});
|
||||
expect(wrapper.state('error')).toEqual(undefined);
|
||||
});
|
||||
|
||||
test('isUserAlreadyLoggedIn: token already available in storage', async () => {
|
||||
|
@ -14,14 +14,26 @@ import '../styles/typeface-roboto.scss';
|
||||
import '../styles/main.scss';
|
||||
import 'normalize.css';
|
||||
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 AppContextConsumer = AppContext.Consumer;
|
||||
|
||||
export default class App extends Component<any, any> {
|
||||
public state = {
|
||||
error: {},
|
||||
export interface AppStateInterface {
|
||||
error?: FormError;
|
||||
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
|
||||
logoUrl: window.VERDACCIO_LOGO,
|
||||
user: {},
|
||||
@ -49,7 +61,7 @@ export default class App extends Component<any, any> {
|
||||
public render(): React.ReactElement<HTMLDivElement> {
|
||||
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 (
|
||||
// @ts-ignore
|
||||
@ -112,7 +124,6 @@ export default class App extends Component<any, any> {
|
||||
this.setState(prevState => ({
|
||||
// @ts-ignore
|
||||
showLoginModal: !prevState.showLoginModal,
|
||||
error: {},
|
||||
}));
|
||||
};
|
||||
|
||||
|
@ -25,7 +25,7 @@ const ACTIONS = {
|
||||
},
|
||||
};
|
||||
|
||||
class ActionBar extends Component<any, any> {
|
||||
class ActionBar extends Component {
|
||||
public render(): ReactElement<HTMLElement> {
|
||||
return (
|
||||
<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 (
|
||||
<a href={link} target={'_blank'}>
|
||||
{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 List from '@material-ui/core/List';
|
||||
@ -8,18 +8,18 @@ import { DetailContextConsumer } from '../../pages/version/Version';
|
||||
import { Heading, AuthorListItem } from './styles';
|
||||
import { isEmail } from '../../utils/url';
|
||||
|
||||
class Authors extends Component<any, any> {
|
||||
render() {
|
||||
class Authors extends Component {
|
||||
public render(): ReactElement<HTMLElement> {
|
||||
return (
|
||||
<DetailContextConsumer>
|
||||
{(context: any) => {
|
||||
{context => {
|
||||
return context && context.packageMeta && this.renderAuthor(context.packageMeta);
|
||||
}}
|
||||
</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) {
|
||||
return avatarComponent;
|
||||
}
|
||||
@ -31,7 +31,7 @@ class Authors extends Component<any, any> {
|
||||
);
|
||||
}
|
||||
|
||||
renderAuthor = packageMeta => {
|
||||
public renderAuthor = packageMeta => {
|
||||
const { author, name: packageName, version } = packageMeta.latest;
|
||||
|
||||
if (!author) {
|
||||
|
@ -7,8 +7,8 @@ import MenuItem from '@material-ui/core/MenuItem';
|
||||
import { fontWeight } from '../../utils/styles/sizes';
|
||||
import { Wrapper, InputField, SuggestionContainer } from './styles';
|
||||
|
||||
export interface Props {
|
||||
suggestions: any[];
|
||||
interface Props {
|
||||
suggestions: unknown[];
|
||||
suggestionsLoading?: boolean;
|
||||
suggestionsLoaded?: boolean;
|
||||
suggestionsError?: boolean;
|
||||
@ -16,17 +16,17 @@ export interface Props {
|
||||
color?: string;
|
||||
value?: string;
|
||||
placeholder?: string;
|
||||
startAdornment?: any;
|
||||
startAdornment?: JSX.Element;
|
||||
disableUnderline?: boolean;
|
||||
onChange?: (event: KeyboardEvent<HTMLInputElement>, { newValue, method }: { newValue: string; method: string }) => void;
|
||||
onSuggestionsFetch?: ({ value: string }) => Promise<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;
|
||||
onBlur?: (event: KeyboardEvent<HTMLInputElement>) => void;
|
||||
}
|
||||
|
||||
const renderInputComponent = inputProps => {
|
||||
const renderInputComponent = (inputProps): JSX.Element => {
|
||||
const { ref, startAdornment, disableUnderline, onKeyDown, ...others } = inputProps;
|
||||
return (
|
||||
<InputField
|
||||
@ -46,7 +46,7 @@ const renderInputComponent = inputProps => {
|
||||
|
||||
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 parts = parse(suggestion.name, matches);
|
||||
return (
|
||||
@ -68,7 +68,7 @@ const renderSuggestion = (suggestion, { query, isHighlighted }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const renderMessage = message => {
|
||||
const renderMessage = (message): JSX.Element => {
|
||||
return (
|
||||
<MenuItem component="div" selected={false}>
|
||||
<div>{message}</div>
|
||||
@ -98,7 +98,7 @@ const AutoComplete = ({
|
||||
suggestionsLoading = false,
|
||||
suggestionsLoaded = false,
|
||||
suggestionsError = false,
|
||||
}: Props) => {
|
||||
}: Props): JSX.Element => {
|
||||
const autosuggestProps = {
|
||||
renderInputComponent,
|
||||
suggestions,
|
||||
@ -119,7 +119,7 @@ const AutoComplete = ({
|
||||
};
|
||||
|
||||
// this format avoid arrow function eslint rule
|
||||
function renderSuggestionsContainer({ containerProps, children, query }) {
|
||||
function renderSuggestionsContainer({ containerProps, children, query }): JSX.Element {
|
||||
return (
|
||||
<SuggestionContainer {...containerProps} square={true}>
|
||||
{suggestionsLoaded && children === null && query && renderMessage(SUGGESTIONS_RESPONSE.NO_RESULT)}
|
||||
|
@ -12,7 +12,7 @@ interface Props {
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
const renderText: React.FC<any> = (text: string, children: React.ReactNode): React.ReactElement<HTMLElement> => {
|
||||
const renderText = (text, children): JSX.Element => {
|
||||
if (children) {
|
||||
return <ClipBoardCopyText>{children}</ClipBoardCopyText>;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
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 { DetailContextConsumer, VersionPageConsumerProps } from '../../pages/version/Version';
|
||||
@ -7,8 +7,19 @@ import { DetailContextConsumer, VersionPageConsumerProps } from '../../pages/ver
|
||||
import { CardWrap, Heading, Tags, Tag } from './styles';
|
||||
import NoItems from '../NoItems';
|
||||
|
||||
class DepDetail extends Component<any, any> {
|
||||
constructor(props: any) {
|
||||
interface DepDetailProps {
|
||||
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);
|
||||
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> {
|
||||
const { dependencies, title } = this.props;
|
||||
const deps = Object.entries(dependencies);
|
||||
const deps = Object.entries(dependencies) as [];
|
||||
|
||||
return (
|
||||
<DetailContextConsumer>
|
||||
{({ enableLoading }: any) => {
|
||||
{({ enableLoading }) => {
|
||||
return (
|
||||
<CardWrap>
|
||||
<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 => {
|
||||
const [name, version] = dep;
|
||||
const [name, version] = dep as [string, string];
|
||||
|
||||
return <WrapperDependencyDetail key={name} name={name} onLoading={enableLoading} version={version} />;
|
||||
});
|
||||
}
|
||||
|
||||
class Dependencies extends Component<any, any> {
|
||||
class Dependencies extends Component {
|
||||
public state = {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@ interface DetailContainerState {
|
||||
tabPosition: number;
|
||||
}
|
||||
|
||||
class DetailContainer extends Component<any, DetailContainerState> {
|
||||
class DetailContainer<P> extends Component<P, DetailContainerState> {
|
||||
public state = {
|
||||
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();
|
||||
this.setState({ tabPosition });
|
||||
};
|
||||
|
@ -11,18 +11,21 @@ import { isEmail } from '../../utils/url';
|
||||
interface Props {
|
||||
type: 'contributors' | 'maintainers';
|
||||
}
|
||||
interface State {
|
||||
visibleDevs: number;
|
||||
}
|
||||
|
||||
class Developers extends Component<Props, any> {
|
||||
state = {
|
||||
class Developers extends Component<Props, State> {
|
||||
public state = {
|
||||
visibleDevs: 6,
|
||||
};
|
||||
|
||||
render() {
|
||||
public render(): JSX.Element {
|
||||
return (
|
||||
<DetailContextConsumer>
|
||||
{({ packageMeta }: any) => {
|
||||
{({ packageMeta }) => {
|
||||
const { type } = this.props;
|
||||
const developerType = packageMeta.latest[type];
|
||||
const developerType = packageMeta && packageMeta.latest[type];
|
||||
if (!developerType || developerType.length === 0) return null;
|
||||
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 }));
|
||||
};
|
||||
|
||||
renderDevelopers = (developers, packageMeta) => {
|
||||
private renderDevelopers = (developers, packageMeta) => {
|
||||
const { type } = this.props;
|
||||
const { visibleDevs } = this.state;
|
||||
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) {
|
||||
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 avatarComponent = <Avatar aria-label={name} src={avatar} />;
|
||||
|
@ -2,22 +2,23 @@ import React, { Component } from 'react';
|
||||
|
||||
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 fileSizeSI from '../../utils/file-size';
|
||||
import { PackageMetaInterface } from 'types/packageMeta';
|
||||
|
||||
class Dist extends Component<any, any> {
|
||||
render() {
|
||||
class Dist extends Component {
|
||||
public render(): JSX.Element {
|
||||
return (
|
||||
<DetailContextConsumer>
|
||||
{(context: any) => {
|
||||
return this.renderDist(context);
|
||||
{(context: Partial<VersionPageConsumerProps>) => {
|
||||
return context && context.packageMeta && this.renderDist(context.packageMeta);
|
||||
}}
|
||||
</DetailContextConsumer>
|
||||
);
|
||||
}
|
||||
|
||||
renderChips(dist: any, license: string) {
|
||||
private renderChips(dist, license: string): JSX.Element | never[] {
|
||||
const distDict = {
|
||||
'file-count': dist.fileCount,
|
||||
size: dist.unpackedSize && fileSizeSI(dist.unpackedSize),
|
||||
@ -43,8 +44,8 @@ class Dist extends Component<any, any> {
|
||||
return chipsList;
|
||||
}
|
||||
|
||||
renderDist = ({ packageMeta }: any) => {
|
||||
const { dist = {}, license } = packageMeta.latest;
|
||||
private renderDist = (packageMeta: PackageMetaInterface) => {
|
||||
const { dist, license } = packageMeta && packageMeta.latest;
|
||||
|
||||
return (
|
||||
<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 { goToVerdaccioWebsite } from '../../utils/windows';
|
||||
|
||||
const renderTooltip = () => (
|
||||
const renderTooltip = (): JSX.Element => (
|
||||
<ToolTip>
|
||||
<Earth name="earth" size="md" />
|
||||
<Flags>
|
||||
@ -22,7 +22,7 @@ const ON_LABEL = 'on';
|
||||
const HEARTH_EMOJI = '♥';
|
||||
|
||||
// @ts-ignore
|
||||
const renderRight = (version = window.VERDACCIO_VERSION) => {
|
||||
const renderRight = (version = window.VERDACCIO_VERSION): JSX.Element => {
|
||||
return (
|
||||
<Right>
|
||||
{POWERED_LABEL}
|
||||
@ -32,7 +32,7 @@ const renderRight = (version = window.VERDACCIO_VERSION) => {
|
||||
);
|
||||
};
|
||||
|
||||
const renderLeft = () => (
|
||||
const renderLeft = (): JSX.Element => (
|
||||
<Left>
|
||||
{MADEWITH_LABEL}
|
||||
<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';
|
||||
|
||||
interface Props {
|
||||
logo: string;
|
||||
logo?: string;
|
||||
username?: string;
|
||||
onLogout: () => void;
|
||||
onToggleLoginModal: () => void;
|
||||
@ -31,7 +31,7 @@ interface Props {
|
||||
}
|
||||
|
||||
interface State {
|
||||
anchorEl?: any;
|
||||
anchorEl?: null | HTMLElement | ((element: HTMLElement) => HTMLElement);
|
||||
openInfoDialog: boolean;
|
||||
registryUrl: string;
|
||||
showMobileNavBar: boolean;
|
||||
|
@ -1,5 +1,6 @@
|
||||
import React, { MouseEvent } from 'react';
|
||||
import capitalize from 'lodash/capitalize';
|
||||
import { Breakpoint } from '@material-ui/core/styles/createBreakpoints';
|
||||
|
||||
import { Svg, Img, ImgWrapper } from './styles';
|
||||
|
||||
@ -57,10 +58,10 @@ export interface Props {
|
||||
name: keyof IconsMap;
|
||||
className?: string;
|
||||
onClick?: (event: MouseEvent<SVGElement | HTMLSpanElement>) => void;
|
||||
size?: 'sm' | 'md';
|
||||
size?: Breakpoint;
|
||||
pointer?: boolean;
|
||||
img?: boolean;
|
||||
modifiers?: any;
|
||||
modifiers?: null | undefined;
|
||||
}
|
||||
|
||||
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 { 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) {
|
||||
case 'md':
|
||||
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;
|
||||
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};
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import ListItemText from '@material-ui/core/ListItemText';
|
||||
import React, { Component } from 'react';
|
||||
|
||||
// @ts-ignore
|
||||
import { DetailContextConsumer } from '../../pages/version/Version';
|
||||
import { DetailContextConsumer, VersionPageConsumerProps } from '../../pages/version/Version';
|
||||
import CopyToClipBoard from '../CopyToClipBoard';
|
||||
|
||||
// logos of package managers
|
||||
@ -14,17 +14,17 @@ import yarn from './img/yarn.svg';
|
||||
import { Heading, InstallItem, PackageMangerAvatar } from './styles';
|
||||
|
||||
class Install extends Component {
|
||||
public render() {
|
||||
public render(): JSX.Element {
|
||||
return (
|
||||
<DetailContextConsumer>
|
||||
{(context: any) => {
|
||||
{(context: Partial<VersionPageConsumerProps>) => {
|
||||
return context && context.packageName && this.renderCopyCLI(context);
|
||||
}}
|
||||
</DetailContextConsumer>
|
||||
);
|
||||
}
|
||||
|
||||
public renderCopyCLI = ({ packageName }: { packageName: string }) => {
|
||||
public renderCopyCLI = ({ packageName = '' }: Partial<VersionPageConsumerProps>) => {
|
||||
return (
|
||||
<>
|
||||
<List subheader={<Heading variant={'subheading'}>{'Installation'}</Heading>}>{this.renderListItems(packageName)}</List>
|
||||
|
@ -6,7 +6,7 @@ interface Props {
|
||||
text: string;
|
||||
capitalize?: boolean;
|
||||
weight?: string;
|
||||
modifiers?: any;
|
||||
modifiers?: null | undefined;
|
||||
}
|
||||
|
||||
const Wrapper = styled('div')`
|
||||
|
@ -63,7 +63,6 @@ describe('<LoginModal />', () => {
|
||||
test('setCredentials - should set username and password in state', () => {
|
||||
const props = {
|
||||
visibility: true,
|
||||
error: {},
|
||||
onCancel: () => {},
|
||||
onSubmit: () => {},
|
||||
};
|
||||
@ -80,7 +79,6 @@ describe('<LoginModal />', () => {
|
||||
test('validateCredentials: should validate credentials', async () => {
|
||||
const props = {
|
||||
visibility: true,
|
||||
error: {},
|
||||
onCancel: () => {},
|
||||
onSubmit: jest.fn(),
|
||||
};
|
||||
@ -89,7 +87,7 @@ describe('<LoginModal />', () => {
|
||||
const instance = wrapper.instance();
|
||||
|
||||
instance.submitCredentials = jest.fn();
|
||||
const { validateCredentials, setCredentials, submitCredentials } = instance;
|
||||
const { handleValidateCredentials, setCredentials, submitCredentials } = instance;
|
||||
|
||||
expect(setCredentials('username', eventUsername)).toBeUndefined();
|
||||
expect(wrapper.state('form').username.value).toEqual('xyz');
|
||||
@ -97,7 +95,7 @@ describe('<LoginModal />', () => {
|
||||
expect(setCredentials('password', eventPassword)).toBeUndefined();
|
||||
expect(wrapper.state('form').password.value).toEqual('1234');
|
||||
|
||||
validateCredentials(event);
|
||||
handleValidateCredentials(event);
|
||||
expect(event.preventDefault).toHaveBeenCalled();
|
||||
expect(wrapper.state('form').username.pristine).toEqual(false);
|
||||
expect(wrapper.state('form').password.pristine).toEqual(false);
|
||||
|
@ -1,5 +1,4 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import DialogTitle from '@material-ui/core/DialogTitle';
|
||||
import Dialog from '@material-ui/core/Dialog';
|
||||
@ -15,22 +14,35 @@ import FormHelperText from '@material-ui/core/FormHelperText';
|
||||
// @ts-ignore
|
||||
import classes from './login.scss';
|
||||
|
||||
export default class LoginModal extends Component<any, any> {
|
||||
static propTypes = {
|
||||
visibility: PropTypes.bool,
|
||||
error: PropTypes.object,
|
||||
onCancel: PropTypes.func,
|
||||
onSubmit: PropTypes.func,
|
||||
};
|
||||
interface FormFields {
|
||||
required: boolean;
|
||||
pristine: boolean;
|
||||
helperText: string;
|
||||
value: string;
|
||||
}
|
||||
export interface FormError {
|
||||
type: string;
|
||||
title: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
error: {},
|
||||
onCancel: () => {},
|
||||
onSubmit: () => {},
|
||||
visibility: true,
|
||||
};
|
||||
interface LoginModalProps {
|
||||
visibility: boolean;
|
||||
error?: FormError;
|
||||
onCancel: () => void;
|
||||
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);
|
||||
this.state = {
|
||||
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
|
||||
* Required to login
|
||||
*/
|
||||
setCredentials = (name, e) => {
|
||||
public setCredentials = (name, e) => {
|
||||
const { form } = this.state;
|
||||
this.setState({
|
||||
form: {
|
||||
@ -69,15 +98,15 @@ export default class LoginModal extends Component<any, any> {
|
||||
});
|
||||
};
|
||||
|
||||
setUsername = event => {
|
||||
public handleUsernameChange = event => {
|
||||
this.setCredentials('username', event);
|
||||
};
|
||||
|
||||
setPassword = event => {
|
||||
public handlePasswordChange = event => {
|
||||
this.setCredentials('password', event);
|
||||
};
|
||||
|
||||
validateCredentials = event => {
|
||||
public handleValidateCredentials = event => {
|
||||
const { form } = this.state;
|
||||
// prevents default submit behavior
|
||||
event.preventDefault();
|
||||
@ -89,7 +118,7 @@ export default class LoginModal extends Component<any, any> {
|
||||
...acc,
|
||||
...{ [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 username = (form.username && form.username.value) || '';
|
||||
const password = (form.password && form.password.value) || '';
|
||||
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
|
||||
// username and password filed to empty state
|
||||
this.setState({
|
||||
@ -112,12 +145,12 @@ export default class LoginModal extends Component<any, any> {
|
||||
...acc,
|
||||
...{ [key]: { ...form[key], value: '', pristine: true } },
|
||||
}),
|
||||
{}
|
||||
{ username: {}, password: {} }
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
renderErrorMessage(title, description) {
|
||||
public renderErrorMessage(title, description): JSX.Element {
|
||||
return (
|
||||
<span>
|
||||
<div>
|
||||
@ -128,7 +161,7 @@ export default class LoginModal extends Component<any, any> {
|
||||
);
|
||||
}
|
||||
|
||||
renderMessage(title, description) {
|
||||
public renderMessage(title, description): JSX.Element {
|
||||
return (
|
||||
<div className={classes.loginErrorMsg} id={'client-snackbar'}>
|
||||
<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)} />;
|
||||
}
|
||||
|
||||
renderNameField = () => {
|
||||
public renderNameField = () => {
|
||||
const {
|
||||
form: { username },
|
||||
} = this.state;
|
||||
return (
|
||||
<FormControl error={!username.value && !username.pristine} fullWidth={true} required={username.required}>
|
||||
<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>}
|
||||
</FormControl>
|
||||
);
|
||||
};
|
||||
|
||||
renderPasswordField = () => {
|
||||
public renderPasswordField = () => {
|
||||
const {
|
||||
form: { password },
|
||||
} = this.state;
|
||||
return (
|
||||
<FormControl error={!password.value && !password.pristine} fullWidth={true} required={password.required} style={{ marginTop: '8px' }}>
|
||||
<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>}
|
||||
</FormControl>
|
||||
);
|
||||
};
|
||||
|
||||
renderActions = () => {
|
||||
public renderActions = () => {
|
||||
const {
|
||||
form: { username, password },
|
||||
} = this.state;
|
||||
@ -183,21 +216,4 @@ export default class LoginModal extends Component<any, any> {
|
||||
</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 { 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 type NotFoundProps = RouteComponentProps & { width: any; history: any };
|
||||
export type NotFoundProps = RouteComponentProps & { width: Breakpoint; history };
|
||||
|
||||
const NotFound: React.FC<NotFoundProps> = ({ history, width }) => {
|
||||
const handleGoTo = (to: string) => () => {
|
||||
const handleGoTo = (to: string): (() => void | undefined) => () => {
|
||||
history.push(to);
|
||||
};
|
||||
|
||||
const handleGoBack = () => () => {
|
||||
const handleGoBack = (): ((e: React.MouseEvent<HTMLElement, MouseEvent>) => void | undefined) => () => {
|
||||
history.goBack();
|
||||
};
|
||||
|
||||
const renderList = () => (
|
||||
const renderList = (): JSX.Element => (
|
||||
<List>
|
||||
<ListItem button={true} divider={true} onClick={handleGoTo('/')}>
|
||||
{'Home'}
|
||||
@ -31,7 +32,7 @@ const NotFound: React.FC<NotFoundProps> = ({ history, width }) => {
|
||||
</List>
|
||||
);
|
||||
|
||||
const renderSubTitle = () => (
|
||||
const renderSubTitle = (): JSX.Element => (
|
||||
<Typography variant="subtitle1">
|
||||
<div>{"The page you're looking for doesn't exist."}</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
|
||||
* @return {object} date object
|
||||
*/
|
||||
const dateOneMonthAgo = () => new Date(1544377770747);
|
||||
const dateOneMonthAgo = (): Date => new Date(1544377770747);
|
||||
|
||||
describe('<Package /> component', () => {
|
||||
test.skip('should load the component', () => {
|
||||
|
@ -23,10 +23,10 @@ interface Dist {
|
||||
unpackedSize: number;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
export interface PackageInterface {
|
||||
name: string;
|
||||
version: string;
|
||||
time: string;
|
||||
time?: number | string;
|
||||
author: Author;
|
||||
description?: string;
|
||||
keywords?: string[];
|
||||
@ -35,6 +35,7 @@ interface Props {
|
||||
bugs?: Bugs;
|
||||
dist?: Dist;
|
||||
}
|
||||
// interface Props {} & PackageInterface;
|
||||
|
||||
import {
|
||||
Author,
|
||||
@ -56,7 +57,7 @@ import {
|
||||
} from './styles';
|
||||
import { isURL } from '../../utils/url';
|
||||
|
||||
const Package: React.FC<Props> = ({
|
||||
const Package: React.FC<PackageInterface> = ({
|
||||
author: { name: authorName, avatar: authorAvatar },
|
||||
bugs,
|
||||
description,
|
||||
|
@ -1,25 +1,21 @@
|
||||
import React, { Fragment } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Fragment, ReactElement } from 'react';
|
||||
|
||||
import Divider from '@material-ui/core/Divider';
|
||||
|
||||
import Package from '../Package';
|
||||
import Help from '../Help';
|
||||
import { formatLicense } from '../../utils/package';
|
||||
import { PackageInterface } from '../Package/Package';
|
||||
|
||||
// @ts-ignore
|
||||
import classes from './packageList.scss';
|
||||
|
||||
interface Props {
|
||||
packages: any;
|
||||
packages: PackageInterface[];
|
||||
}
|
||||
|
||||
export default class PackageList extends React.Component<Props, {}> {
|
||||
static propTypes = {
|
||||
packages: PropTypes.array,
|
||||
};
|
||||
|
||||
render() {
|
||||
public render(): ReactElement<HTMLElement> {
|
||||
return (
|
||||
<div className={'package-list-items'}>
|
||||
<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;
|
||||
return packages.length > 0;
|
||||
}
|
||||
|
||||
renderPackages = () => {
|
||||
private renderPackages = () => {
|
||||
return <>{this.renderList()}</>;
|
||||
};
|
||||
|
||||
renderList = () => {
|
||||
private renderList = () => {
|
||||
const { packages } = this.props;
|
||||
return packages.map((pkg, i) => {
|
||||
const { name, version, description, time, keywords, dist, homepage, bugs } = pkg;
|
||||
const author = pkg.author;
|
||||
const { name, version, description, time, keywords, dist, homepage, bugs, author } = pkg;
|
||||
// TODO: move format license to API side.
|
||||
const license = formatLicense(pkg.license);
|
||||
return (
|
||||
|
@ -11,7 +11,7 @@ import { getCLISetRegistry, getCLIChangePassword, getCLISetConfigRegistry } from
|
||||
import { NODE_MANAGER } from '../../utils/constants';
|
||||
|
||||
/* eslint react/prop-types:0 */
|
||||
function TabContainer({ children }) {
|
||||
function TabContainer({ children }): JSX.Element {
|
||||
return (
|
||||
<CommandContainer>
|
||||
<Typography component="div" style={{ padding: 0, minHeight: 170 }}>
|
||||
@ -22,15 +22,20 @@ function TabContainer({ children }) {
|
||||
}
|
||||
|
||||
class RegistryInfoContent extends Component<Props, State> {
|
||||
state = {
|
||||
public state = {
|
||||
tabPosition: 0,
|
||||
};
|
||||
|
||||
render() {
|
||||
public render(): JSX.Element {
|
||||
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 { 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 (
|
||||
<React.Fragment>
|
||||
<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 (
|
||||
<React.Fragment>
|
||||
<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 (
|
||||
<React.Fragment>
|
||||
<CopyToClipBoard text={getCLISetConfigRegistry(`${NODE_MANAGER.yarn} config set`, scope, registryUrl)} />
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
handleChange = (event: any, tabPosition: number) => {
|
||||
event.preventDefault();
|
||||
this.setState({ tabPosition });
|
||||
};
|
||||
}
|
||||
|
||||
export default RegistryInfoContent;
|
||||
|
@ -8,7 +8,7 @@ import { Props } from './types';
|
||||
|
||||
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}>
|
||||
<Title disableTypography={true}>{'Register Info'}</Title>
|
||||
<Content>{children}</Content>
|
||||
|
@ -13,7 +13,7 @@ import { Heading, GithubLink, RepositoryListItem } from './styles';
|
||||
import git from './img/git.png';
|
||||
import { isURL } from '../../utils/url';
|
||||
|
||||
class Repository extends Component<any, any> {
|
||||
class Repository extends Component {
|
||||
public render(): ReactElement<HTMLElement> {
|
||||
return (
|
||||
<DetailContextConsumer>
|
||||
@ -33,12 +33,7 @@ class Repository extends Component<any, any> {
|
||||
}
|
||||
|
||||
private renderRepository = packageMeta => {
|
||||
const {
|
||||
repository: {
|
||||
// @ts-ignore
|
||||
url,
|
||||
} = {},
|
||||
} = packageMeta.latest;
|
||||
const { repository: { url = null } = {} } = packageMeta.latest;
|
||||
|
||||
if (!url || isURL(url) === false) {
|
||||
return null;
|
||||
|
@ -154,7 +154,7 @@ describe('<Search /> component test', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
jest.doMock('lodash/debounce', () => {
|
||||
return function debounceMock(fn, delay) {
|
||||
return function debounceMock(fn) {
|
||||
return fn;
|
||||
};
|
||||
});
|
||||
|
@ -11,11 +11,15 @@ import colors from '../../utils/styles/colors';
|
||||
|
||||
export interface State {
|
||||
search: string;
|
||||
suggestions: any[];
|
||||
suggestions: unknown[];
|
||||
loading: boolean;
|
||||
loaded: boolean;
|
||||
error: boolean;
|
||||
}
|
||||
interface AbortControllerInterface {
|
||||
signal: () => void;
|
||||
abort: () => void;
|
||||
}
|
||||
|
||||
export type cancelAllSearchRequests = () => void;
|
||||
export type handlePackagesClearRequested = () => void;
|
||||
@ -31,8 +35,6 @@ const CONSTANTS = {
|
||||
};
|
||||
|
||||
export class Search extends Component<RouteComponentProps<{}>, State> {
|
||||
private requestList: any[];
|
||||
|
||||
constructor(props: RouteComponentProps<{}>) {
|
||||
super(props);
|
||||
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.
|
||||
*/
|
||||
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;
|
||||
// stops event bubbling
|
||||
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 (
|
||||
<InputAdornment position={'start'} style={{ color: colors.white }}>
|
||||
<IconSearch />
|
||||
|
@ -1,5 +1,5 @@
|
||||
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';
|
||||
|
||||
@ -8,7 +8,7 @@ export const Wrapper = styled('div')`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
${(props): Themed<any, object> =>
|
||||
${props =>
|
||||
// @ts-ignore
|
||||
props.centered &&
|
||||
css`
|
||||
|
@ -8,7 +8,7 @@ import { formatDateDistance } from '../../utils/package';
|
||||
|
||||
import { Heading, Spacer, ListItemText } from './styles';
|
||||
|
||||
class UpLinks extends React.PureComponent<any> {
|
||||
class UpLinks extends React.PureComponent<{}> {
|
||||
public render(): ReactElement<HTMLElement> {
|
||||
return (
|
||||
<DetailContextConsumer>
|
||||
|
@ -8,7 +8,7 @@ import { DIST_TAGS } from '../../../lib/constants';
|
||||
|
||||
const NOT_AVAILABLE = 'Not available';
|
||||
|
||||
class Versions extends React.PureComponent<any> {
|
||||
class Versions extends React.PureComponent {
|
||||
public render(): ReactElement<HTMLDivElement> {
|
||||
return (
|
||||
<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 (
|
||||
<List>
|
||||
{Object.keys(packages)
|
||||
|
@ -8,7 +8,7 @@ import App from './App';
|
||||
|
||||
const rootNode = document.getElementById('root');
|
||||
|
||||
const renderApp = Component => {
|
||||
const renderApp = (Component): void => {
|
||||
ReactDOM.render(
|
||||
<AppContainer>
|
||||
<Component />
|
||||
|
@ -4,7 +4,7 @@ import PackageList from '../../components/PackageList';
|
||||
|
||||
interface Props {
|
||||
isUserLoggedIn: boolean;
|
||||
packages: any[];
|
||||
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 DetailContainer from '../../components/DetailContainer/DetailContainer';
|
||||
import DetailSidebar from '../../components/DetailSidebar/DetailSidebar';
|
||||
import { callDetailPage, DetailPage } from '../../utils/calls';
|
||||
import { callDetailPage } from '../../utils/calls';
|
||||
import { getRouterPackageName } from '../../utils/package';
|
||||
import NotFound from '../../components/NotFound';
|
||||
import { PackageMetaInterface } from '../../../types/packageMeta';
|
||||
|
||||
export interface DetailContextProps {
|
||||
packageMeta: any;
|
||||
readMe: any;
|
||||
packageMeta: PackageMetaInterface;
|
||||
readMe: string;
|
||||
packageName: string;
|
||||
enableLoading: () => void;
|
||||
}
|
||||
|
||||
export const DetailContext = React.createContext<DetailContextProps | null>(null);
|
||||
export const DetailContext = React.createContext<Partial<DetailContextProps>>({});
|
||||
|
||||
export interface VersionPageConsumerProps {
|
||||
packageMeta: any;
|
||||
readMe: any;
|
||||
packageName: any;
|
||||
enableLoading: any;
|
||||
packageMeta: PackageMetaInterface;
|
||||
readMe: string;
|
||||
packageName: string;
|
||||
enableLoading: () => void;
|
||||
}
|
||||
|
||||
export const DetailContextProvider: Provider<VersionPageConsumerProps | null> = DetailContext.Provider;
|
||||
export const DetailContextConsumer: Consumer<VersionPageConsumerProps | null> = DetailContext.Consumer;
|
||||
export const DetailContextProvider: Provider<Partial<VersionPageConsumerProps>> = DetailContext.Provider;
|
||||
export const DetailContextConsumer: Consumer<Partial<VersionPageConsumerProps>> = DetailContext.Consumer;
|
||||
|
||||
class VersionPage extends Component<any, any> {
|
||||
constructor(props: any) {
|
||||
interface PropsInterface {
|
||||
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);
|
||||
|
||||
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 packageName = getRouterPackageName(match);
|
||||
|
||||
@ -65,10 +78,10 @@ class VersionPage extends Component<any, any> {
|
||||
}
|
||||
|
||||
/* 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;
|
||||
if (packageName !== prevState.packageName) {
|
||||
const { readMe, packageMeta } = await callDetailPage(packageName);
|
||||
const { readMe, packageMeta } = (await callDetailPage(packageName)) as Partial<StateInterface>;
|
||||
this.setState({
|
||||
readMe,
|
||||
packageMeta,
|
||||
@ -112,7 +125,7 @@ class VersionPage extends Component<any, any> {
|
||||
});
|
||||
|
||||
try {
|
||||
const { readMe, packageMeta } = await callDetailPage(packageName);
|
||||
const { readMe, packageMeta } = (await callDetailPage(packageName)) as Partial<StateInterface>;
|
||||
this.setState({
|
||||
readMe,
|
||||
packageMeta,
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
import React, { Component, ReactElement } from 'react';
|
||||
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 history from './history';
|
||||
@ -12,7 +12,12 @@ const NotFound = asyncComponent(() => import('./components/NotFound'));
|
||||
const VersionPackage = asyncComponent(() => import('./pages/version/Version'));
|
||||
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> {
|
||||
return (
|
||||
<Router history={history}>
|
||||
@ -34,8 +39,8 @@ class RouterApp extends Component<any, any> {
|
||||
|
||||
return (
|
||||
<AppContextConsumer>
|
||||
{function renderConsumerVersionPage({ logoUrl, scope, user }: any) {
|
||||
return <Header logo={logoUrl} onLogout={onLogout} onToggleLoginModal={onToggleLoginModal} scope={scope} username={user.username} />;
|
||||
{function renderConsumerVersionPage({ logoUrl, scope = '', user }: Partial<AppStateInterface>) {
|
||||
return <Header logo={logoUrl} onLogout={onLogout} onToggleLoginModal={onToggleLoginModal} scope={scope} username={user && user.username} />;
|
||||
}}
|
||||
</AppContextConsumer>
|
||||
);
|
||||
@ -44,7 +49,7 @@ class RouterApp extends Component<any, any> {
|
||||
public renderHomePage = (): ReactElement<HTMLDivElement> => {
|
||||
return (
|
||||
<AppContextConsumer>
|
||||
{function renderConsumerVersionPage({ isUserLoggedIn, packages }: any) {
|
||||
{function renderConsumerVersionPage({ isUserLoggedIn, packages }: Partial<AppStateInterface>) {
|
||||
// @ts-ignore
|
||||
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 (
|
||||
<AppContextConsumer>
|
||||
{function renderConsumerVersionPage({ isUserLoggedIn }: any) {
|
||||
{function renderConsumerVersionPage({ isUserLoggedIn }: Partial<AppStateInterface>) {
|
||||
return <VersionPackage {...routerProps} isUserLoggedIn={isUserLoggedIn} />;
|
||||
}}
|
||||
</AppContextConsumer>
|
||||
|
@ -6,9 +6,9 @@ import '../../types';
|
||||
* @param {object} response
|
||||
* @returns {promise}
|
||||
*/
|
||||
function handleResponseType(response): Promise<any> {
|
||||
function handleResponseType(response: Response): Promise<[boolean, Blob | string]> | Promise<void> {
|
||||
if (response.headers) {
|
||||
const contentType = response.headers.get('Content-Type');
|
||||
const contentType = response.headers.get('Content-Type') as string;
|
||||
if (contentType.includes('application/pdf')) {
|
||||
return Promise.all([response.ok, response.blob()]);
|
||||
}
|
||||
@ -25,16 +25,16 @@ function handleResponseType(response): Promise<any> {
|
||||
}
|
||||
|
||||
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) {
|
||||
throw new Error('VERDACCIO_API_URL is not defined!');
|
||||
}
|
||||
|
||||
const token = storage.getItem('token');
|
||||
if (token) {
|
||||
if (!options.headers) options.headers = {};
|
||||
|
||||
options.headers.authorization = `Bearer ${token}`;
|
||||
const headers = new Headers(options && options.headers);
|
||||
if (token && options && options.headers) {
|
||||
headers.set('Authorization', `Bearer ${token}`);
|
||||
options.headers = Object.assign(options.headers, headers);
|
||||
}
|
||||
|
||||
if (!['http://', 'https://', '//'].some(prefix => url.startsWith(prefix))) {
|
||||
@ -42,7 +42,7 @@ class API {
|
||||
url = window.VERDACCIO_API_URL + url;
|
||||
}
|
||||
|
||||
return new Promise<any>((resolve, reject) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
fetch(url, {
|
||||
method,
|
||||
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 {
|
||||
static Component = null;
|
||||
state = { Component: AsyncComponent.Component };
|
||||
public static Component = null;
|
||||
public state = { Component: AsyncComponent.Component };
|
||||
|
||||
componentDidMount() {
|
||||
public componentDidMount(): void {
|
||||
const { Component } = this.state;
|
||||
if (!Component) {
|
||||
getComponent()
|
||||
@ -19,7 +19,8 @@ export function asyncComponent(getComponent) {
|
||||
});
|
||||
}
|
||||
}
|
||||
render() {
|
||||
|
||||
public render(): JSX.Element | null {
|
||||
const { Component } = this.state;
|
||||
if (Component) {
|
||||
// eslint-disable-next-line verdaccio/jsx-spread
|
||||
|
@ -1,8 +1,9 @@
|
||||
import API from './api';
|
||||
import { PackageMetaInterface } from 'types/packageMeta';
|
||||
|
||||
export interface DetailPage {
|
||||
readMe: any;
|
||||
packageMeta: any;
|
||||
readMe: string | {};
|
||||
packageMeta: PackageMetaInterface | {};
|
||||
}
|
||||
|
||||
export async function callDetailPage(packageName): Promise<DetailPage> {
|
||||
|
@ -1,6 +1,6 @@
|
||||
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();
|
||||
|
||||
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');
|
||||
}
|
||||
|
@ -5,30 +5,34 @@ import { Base64 } from 'js-base64';
|
||||
import API from './api';
|
||||
import { HEADERS } from '../../lib/constants';
|
||||
|
||||
export function isTokenExpire(token?: any) {
|
||||
interface PayloadInterface {
|
||||
exp: number;
|
||||
}
|
||||
|
||||
export function isTokenExpire(token?: string): boolean {
|
||||
if (!isString(token)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let [, payload]: any = token.split('.');
|
||||
const [, payload] = token.split('.');
|
||||
|
||||
if (!payload) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let exp: number;
|
||||
try {
|
||||
payload = JSON.parse(Base64.decode(payload));
|
||||
exp = JSON.parse(Base64.decode(payload)).exp;
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line
|
||||
console.error('Invalid token:', error, token);
|
||||
console.error('Invalid token:', error, token);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!payload.exp || !isNumber(payload.exp)) {
|
||||
if (!exp || !isNumber(exp)) {
|
||||
return true;
|
||||
}
|
||||
// Report as expire before (real expire time - 30s)
|
||||
const jsTimestamp = payload.exp * 1000 - 30000;
|
||||
const jsTimestamp = exp * 1000 - 30000;
|
||||
const expired = Date.now() >= jsTimestamp;
|
||||
|
||||
return expired;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import parseXSS from 'xss';
|
||||
|
||||
export function preventXSS(text: string) {
|
||||
export function preventXSS(text: string): string {
|
||||
const encodedText = parseXSS.filterXSS(text);
|
||||
|
||||
return encodedText;
|
||||
|
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* CSS to represent truncated text with an ellipsis.
|
||||
*/
|
||||
export function ellipsis(width: string | number) {
|
||||
export function ellipsis(width: string | number): {} {
|
||||
return {
|
||||
display: 'inline-block',
|
||||
maxWidth: width,
|
||||
@ -24,7 +24,7 @@ interface SpacingShortHand<type> {
|
||||
|
||||
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 valuesWithDefaults = [firstValue, secondValue, thirdValue, fourthValue];
|
||||
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
|
||||
declare module '*.svg' {
|
||||
const content: any;
|
||||
const content: string;
|
||||
export default content;
|
||||
}
|
||||
|
||||
declare module '*.png' {
|
||||
const content: any;
|
||||
export default content;
|
||||
}
|
||||
declare module '*.png';
|
||||
|
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