mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2025-01-10 02:07:26 +03:00
More elements migrated to typescript
This commit is contained in:
parent
62df46d648
commit
2eba607874
9 changed files with 47 additions and 45 deletions
|
@ -1,23 +1,23 @@
|
||||||
import React from 'react';
|
import React, { ChangeEvent, FC } from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
|
import { identity } from 'ramda';
|
||||||
|
|
||||||
export const basePropTypes = {
|
export interface BooleanControlProps {
|
||||||
checked: PropTypes.bool.isRequired,
|
checked?: boolean;
|
||||||
onChange: PropTypes.func.isRequired,
|
onChange?: (checked: boolean, e: ChangeEvent<HTMLInputElement>) => void;
|
||||||
children: PropTypes.oneOfType([ PropTypes.string, PropTypes.node ]),
|
className?: string;
|
||||||
className: PropTypes.string,
|
}
|
||||||
};
|
|
||||||
|
|
||||||
const propTypes = {
|
interface BooleanControlWithTypeProps extends BooleanControlProps {
|
||||||
...basePropTypes,
|
type: 'switch' | 'checkbox';
|
||||||
type: PropTypes.oneOf([ 'switch', 'checkbox' ]).isRequired,
|
}
|
||||||
};
|
|
||||||
|
|
||||||
const BooleanControl = ({ checked, onChange, className, children, type }) => {
|
const BooleanControl: FC<BooleanControlWithTypeProps> = (
|
||||||
|
{ checked = false, onChange = identity, className, children, type },
|
||||||
|
) => {
|
||||||
const id = uuid();
|
const id = uuid();
|
||||||
const onChecked = (e) => onChange(e.target.checked, e);
|
const onChecked = (e: ChangeEvent<HTMLInputElement>) => onChange(e.target.checked, e);
|
||||||
const typeClasses = {
|
const typeClasses = {
|
||||||
'custom-switch': type === 'switch',
|
'custom-switch': type === 'switch',
|
||||||
'custom-checkbox': type === 'checkbox',
|
'custom-checkbox': type === 'checkbox',
|
||||||
|
@ -31,6 +31,4 @@ const BooleanControl = ({ checked, onChange, className, children, type }) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
BooleanControl.propTypes = propTypes;
|
|
||||||
|
|
||||||
export default BooleanControl;
|
export default BooleanControl;
|
|
@ -1,8 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import BooleanControl, { basePropTypes } from './BooleanControl';
|
|
||||||
|
|
||||||
const Checkbox = (props) => <BooleanControl type="checkbox" {...props} />;
|
|
||||||
|
|
||||||
Checkbox.propTypes = basePropTypes;
|
|
||||||
|
|
||||||
export default Checkbox;
|
|
6
src/utils/Checkbox.tsx
Normal file
6
src/utils/Checkbox.tsx
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import React, { FC } from 'react';
|
||||||
|
import BooleanControl, { BooleanControlProps } from './BooleanControl';
|
||||||
|
|
||||||
|
const Checkbox: FC<BooleanControlProps> = (props) => <BooleanControl type="checkbox" {...props} />;
|
||||||
|
|
||||||
|
export default Checkbox;
|
|
@ -1,8 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import BooleanControl, { basePropTypes } from './BooleanControl';
|
|
||||||
|
|
||||||
const ToggleSwitch = (props) => <BooleanControl type="switch" {...props} />;
|
|
||||||
|
|
||||||
ToggleSwitch.propTypes = basePropTypes;
|
|
||||||
|
|
||||||
export default ToggleSwitch;
|
|
6
src/utils/ToggleSwitch.tsx
Normal file
6
src/utils/ToggleSwitch.tsx
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import React, { FC } from 'react';
|
||||||
|
import BooleanControl, { BooleanControlProps } from './BooleanControl';
|
||||||
|
|
||||||
|
const ToggleSwitch: FC<BooleanControlProps> = (props) => <BooleanControl type="switch" {...props} />;
|
||||||
|
|
||||||
|
export default ToggleSwitch;
|
|
@ -1,6 +1,6 @@
|
||||||
import bowser from 'bowser';
|
import bowser from 'bowser';
|
||||||
import { zipObj } from 'ramda';
|
import { zipObj } from 'ramda';
|
||||||
import { hasValue } from '../utils';
|
import { Empty, hasValue } from '../utils';
|
||||||
|
|
||||||
const DEFAULT = 'Others';
|
const DEFAULT = 'Others';
|
||||||
const BROWSERS_WHITELIST = [
|
const BROWSERS_WHITELIST = [
|
||||||
|
@ -17,17 +17,22 @@ const BROWSERS_WHITELIST = [
|
||||||
'WeChat',
|
'WeChat',
|
||||||
];
|
];
|
||||||
|
|
||||||
export const parseUserAgent = (userAgent) => {
|
interface UserAgent {
|
||||||
|
browser: string;
|
||||||
|
os: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const parseUserAgent = (userAgent: string | Empty): UserAgent => {
|
||||||
if (!hasValue(userAgent)) {
|
if (!hasValue(userAgent)) {
|
||||||
return { browser: DEFAULT, os: DEFAULT };
|
return { browser: DEFAULT, os: DEFAULT };
|
||||||
}
|
}
|
||||||
|
|
||||||
const { browser: { name: browser }, os: { name: os } } = bowser.parse(userAgent);
|
const { browser: { name: browser }, os: { name: os } } = bowser.parse(userAgent);
|
||||||
|
|
||||||
return { os: os || DEFAULT, browser: browser && BROWSERS_WHITELIST.includes(browser) ? browser : DEFAULT };
|
return { os: os ?? DEFAULT, browser: browser && BROWSERS_WHITELIST.includes(browser) ? browser : DEFAULT };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const extractDomain = (url) => {
|
export const extractDomain = (url: string | Empty): string => {
|
||||||
if (!hasValue(url)) {
|
if (!hasValue(url)) {
|
||||||
return 'Direct';
|
return 'Direct';
|
||||||
}
|
}
|
||||||
|
@ -37,4 +42,5 @@ export const extractDomain = (url) => {
|
||||||
return domain.split(':')[0];
|
return domain.split(':')[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const fillTheGaps = (stats, labels) => Object.values({ ...zipObj(labels, labels.map(() => 0)), ...stats });
|
export const fillTheGaps = (stats: Record<string, number>, labels: string[]): number[] =>
|
||||||
|
Object.values({ ...zipObj(labels, labels.map(() => 0)), ...stats });
|
|
@ -2,7 +2,7 @@ import { isEmpty, isNil, range } from 'ramda';
|
||||||
|
|
||||||
export type OrderDir = 'ASC' | 'DESC' | undefined;
|
export type OrderDir = 'ASC' | 'DESC' | undefined;
|
||||||
|
|
||||||
export const determineOrderDir = (currentField: string, newField: string, currentOrderDir: OrderDir): OrderDir => {
|
export const determineOrderDir = (currentField: string, newField: string, currentOrderDir?: OrderDir): OrderDir => {
|
||||||
if (currentField !== newField) {
|
if (currentField !== newField) {
|
||||||
return 'ASC';
|
return 'ASC';
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,19 @@
|
||||||
import React from 'react';
|
import React, { ChangeEvent, PropsWithChildren } from 'react';
|
||||||
import { mount } from 'enzyme';
|
import { mount, ReactWrapper } from 'enzyme';
|
||||||
|
import { Mock } from 'ts-mockery';
|
||||||
import Checkbox from '../../src/utils/Checkbox';
|
import Checkbox from '../../src/utils/Checkbox';
|
||||||
|
import { BooleanControlProps } from '../../src/utils/BooleanControl';
|
||||||
|
|
||||||
describe('<Checkbox />', () => {
|
describe('<Checkbox />', () => {
|
||||||
let wrapped;
|
let wrapped: ReactWrapper;
|
||||||
|
|
||||||
const createComponent = (props = {}) => {
|
const createComponent = (props: PropsWithChildren<BooleanControlProps> = {}) => {
|
||||||
wrapped = mount(<Checkbox {...props} />);
|
wrapped = mount(<Checkbox {...props} />);
|
||||||
|
|
||||||
return wrapped;
|
return wrapped;
|
||||||
};
|
};
|
||||||
|
|
||||||
afterEach(() => wrapped && wrapped.unmount());
|
afterEach(() => wrapped?.unmount());
|
||||||
|
|
||||||
it('includes extra class names when provided', () => {
|
it('includes extra class names when provided', () => {
|
||||||
const classNames = [ 'foo', 'bar', 'baz' ];
|
const classNames = [ 'foo', 'bar', 'baz' ];
|
||||||
|
@ -55,11 +57,11 @@ describe('<Checkbox />', () => {
|
||||||
|
|
||||||
it('changes checked status on input change', () => {
|
it('changes checked status on input change', () => {
|
||||||
const onChange = jest.fn();
|
const onChange = jest.fn();
|
||||||
const e = { target: { checked: false } };
|
const e = Mock.of<ChangeEvent<HTMLInputElement>>({ target: { checked: false } });
|
||||||
const wrapped = createComponent({ checked: true, onChange });
|
const wrapped = createComponent({ checked: true, onChange });
|
||||||
const input = wrapped.find('input');
|
const input = wrapped.find('input');
|
||||||
|
|
||||||
input.prop('onChange')(e);
|
(input.prop('onChange') as Function)(e);
|
||||||
|
|
||||||
expect(onChange).toHaveBeenCalledWith(false, e);
|
expect(onChange).toHaveBeenCalledWith(false, e);
|
||||||
});
|
});
|
Loading…
Reference in a new issue