mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2024-12-23 09:30:31 +03:00
Finished migrating all remaining utils to TS
This commit is contained in:
parent
f8ea1ae3d5
commit
16d96efa4a
11 changed files with 95 additions and 105 deletions
|
@ -17,7 +17,7 @@ interface SearchBarProps {
|
||||||
shortUrlsListParams: ShortUrlsListParams;
|
shortUrlsListParams: ShortUrlsListParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
const dateOrUndefined = (date?: string) => date ? moment(date) : undefined;
|
const dateOrNull = (date?: string) => date ? moment(date) : null;
|
||||||
|
|
||||||
const SearchBar = (colorGenerator: ColorGenerator, ForServerVersion: FC<Versions>) => (
|
const SearchBar = (colorGenerator: ColorGenerator, ForServerVersion: FC<Versions>) => (
|
||||||
{ listShortUrls, shortUrlsListParams }: SearchBarProps,
|
{ listShortUrls, shortUrlsListParams }: SearchBarProps,
|
||||||
|
@ -41,8 +41,8 @@ const SearchBar = (colorGenerator: ColorGenerator, ForServerVersion: FC<Versions
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-lg-8 offset-lg-4 col-xl-6 offset-xl-6">
|
<div className="col-lg-8 offset-lg-4 col-xl-6 offset-xl-6">
|
||||||
<DateRangeRow
|
<DateRangeRow
|
||||||
startDate={dateOrUndefined(shortUrlsListParams.startDate)}
|
startDate={dateOrNull(shortUrlsListParams.startDate)}
|
||||||
endDate={dateOrUndefined(shortUrlsListParams.endDate)}
|
endDate={dateOrNull(shortUrlsListParams.endDate)}
|
||||||
onStartDateChange={setDate('startDate')}
|
onStartDateChange={setDate('startDate')}
|
||||||
onEndDateChange={setDate('endDate')}
|
onEndDateChange={setDate('endDate')}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -62,9 +62,9 @@ const ShortUrlsList = (ShortUrlsRow: FC<ShortUrlsRowProps>) => ({
|
||||||
orderDir: orderBy && head(values(orderBy)),
|
orderDir: orderBy && head(values(orderBy)),
|
||||||
});
|
});
|
||||||
const refreshList = (extraParams: ShortUrlsListParams) => listShortUrls({ ...shortUrlsListParams, ...extraParams });
|
const refreshList = (extraParams: ShortUrlsListParams) => listShortUrls({ ...shortUrlsListParams, ...extraParams });
|
||||||
const handleOrderBy = (orderField: OrderableFields, orderDir: OrderDir) => {
|
const handleOrderBy = (orderField?: OrderableFields, orderDir?: OrderDir) => {
|
||||||
setOrder({ orderField, orderDir });
|
setOrder({ orderField, orderDir });
|
||||||
refreshList({ orderBy: { [orderField]: orderDir } });
|
refreshList({ orderBy: orderField ? { [orderField]: orderDir } : undefined });
|
||||||
};
|
};
|
||||||
const orderByColumn = (field: OrderableFields) => () =>
|
const orderByColumn = (field: OrderableFields) => () =>
|
||||||
handleOrderBy(field, determineOrderDir(field, order.orderField, order.orderDir));
|
handleOrderBy(field, determineOrderDir(field, order.orderField, order.orderDir));
|
||||||
|
|
|
@ -1,25 +1,26 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import moment from 'moment';
|
||||||
import DateInput from './DateInput';
|
import DateInput from './DateInput';
|
||||||
import './DateRangeRow.scss';
|
import './DateRangeRow.scss';
|
||||||
|
|
||||||
const dateType = PropTypes.oneOfType([ PropTypes.string, PropTypes.object ]);
|
interface DateRangeRowProps {
|
||||||
const propTypes = {
|
startDate?: moment.Moment | null;
|
||||||
startDate: dateType,
|
endDate?: moment.Moment | null;
|
||||||
endDate: dateType,
|
onStartDateChange: (date: moment.Moment | null) => void;
|
||||||
onStartDateChange: PropTypes.func.isRequired,
|
onEndDateChange: (date: moment.Moment | null) => void;
|
||||||
onEndDateChange: PropTypes.func.isRequired,
|
disabled?: boolean;
|
||||||
disabled: PropTypes.bool,
|
}
|
||||||
};
|
|
||||||
|
|
||||||
const DateRangeRow = ({ startDate, endDate, onStartDateChange, onEndDateChange, disabled = false }) => (
|
const DateRangeRow = (
|
||||||
|
{ startDate = null, endDate = null, disabled = false, onStartDateChange, onEndDateChange }: DateRangeRowProps,
|
||||||
|
) => (
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-md-6">
|
<div className="col-md-6">
|
||||||
<DateInput
|
<DateInput
|
||||||
selected={startDate}
|
selected={startDate}
|
||||||
placeholderText="Since"
|
placeholderText="Since"
|
||||||
isClearable
|
isClearable
|
||||||
maxDate={endDate}
|
maxDate={endDate ?? undefined}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onChange={onStartDateChange}
|
onChange={onStartDateChange}
|
||||||
/>
|
/>
|
||||||
|
@ -30,7 +31,7 @@ const DateRangeRow = ({ startDate, endDate, onStartDateChange, onEndDateChange,
|
||||||
selected={endDate}
|
selected={endDate}
|
||||||
placeholderText="Until"
|
placeholderText="Until"
|
||||||
isClearable
|
isClearable
|
||||||
minDate={startDate}
|
minDate={startDate ?? undefined}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onChange={onEndDateChange}
|
onChange={onEndDateChange}
|
||||||
/>
|
/>
|
||||||
|
@ -38,6 +39,4 @@ const DateRangeRow = ({ startDate, endDate, onStartDateChange, onEndDateChange,
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
DateRangeRow.propTypes = propTypes;
|
|
||||||
|
|
||||||
export default DateRangeRow;
|
export default DateRangeRow;
|
|
@ -1,17 +1,18 @@
|
||||||
import React from 'react';
|
import React, { FC } from 'react';
|
||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
import PropTypes from 'prop-types';
|
import { InputType } from 'reactstrap/lib/Input';
|
||||||
|
|
||||||
const propTypes = {
|
interface HorizontalFormGroupProps {
|
||||||
children: PropTypes.node.isRequired,
|
value: string;
|
||||||
value: PropTypes.string.isRequired,
|
onChange: (newValue: string) => void;
|
||||||
onChange: PropTypes.func.isRequired,
|
id?: string;
|
||||||
id: PropTypes.string,
|
type?: InputType;
|
||||||
type: PropTypes.string,
|
required?: boolean;
|
||||||
required: PropTypes.bool,
|
}
|
||||||
};
|
|
||||||
|
|
||||||
export const HorizontalFormGroup = ({ children, value, onChange, id = uuid(), type = 'text', required = true }) => (
|
export const HorizontalFormGroup: FC<HorizontalFormGroupProps> = (
|
||||||
|
{ children, value, onChange, id = uuid(), type = 'text', required = true },
|
||||||
|
) => (
|
||||||
<div className="form-group row">
|
<div className="form-group row">
|
||||||
<label htmlFor={id} className="col-lg-1 col-md-2 col-form-label create-server__label">
|
<label htmlFor={id} className="col-lg-1 col-md-2 col-form-label create-server__label">
|
||||||
{children}:
|
{children}:
|
||||||
|
@ -28,5 +29,3 @@ export const HorizontalFormGroup = ({ children, value, onChange, id = uuid(), ty
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
HorizontalFormGroup.propTypes = propTypes;
|
|
|
@ -1,33 +1,35 @@
|
||||||
import React from 'react';
|
import React, { FC } from 'react';
|
||||||
import { Card } from 'reactstrap';
|
import { Card } from 'reactstrap';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { faCircleNotch as preloader } from '@fortawesome/free-solid-svg-icons';
|
import { faCircleNotch as preloader } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
|
|
||||||
const getClassForType = (type) => {
|
type MessageType = 'default' | 'error';
|
||||||
const map = {
|
|
||||||
|
const getClassForType = (type: MessageType) => {
|
||||||
|
const map: Record<MessageType, string> = {
|
||||||
error: 'border-danger',
|
error: 'border-danger',
|
||||||
|
default: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
return map[type] || '';
|
return map[type];
|
||||||
};
|
};
|
||||||
const getTextClassForType = (type) => {
|
const getTextClassForType = (type: MessageType) => {
|
||||||
const map = {
|
const map: Record<MessageType, string> = {
|
||||||
error: 'text-danger',
|
error: 'text-danger',
|
||||||
|
default: 'text-muted',
|
||||||
};
|
};
|
||||||
|
|
||||||
return map[type] || 'text-muted';
|
return map[type];
|
||||||
};
|
};
|
||||||
|
|
||||||
const propTypes = {
|
interface MessageProps {
|
||||||
noMargin: PropTypes.bool,
|
noMargin?: boolean;
|
||||||
loading: PropTypes.bool,
|
loading?: boolean;
|
||||||
children: PropTypes.node,
|
type?: MessageType;
|
||||||
type: PropTypes.oneOf([ 'default', 'error' ]),
|
}
|
||||||
};
|
|
||||||
|
|
||||||
const Message = ({ children, loading = false, noMargin = false, type = 'default' }) => {
|
const Message: FC<MessageProps> = ({ children, loading = false, noMargin = false, type = 'default' }) => {
|
||||||
const cardClasses = classNames('bg-light', getClassForType(type), { 'mt-4': !noMargin });
|
const cardClasses = classNames('bg-light', getClassForType(type), { 'mt-4': !noMargin });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -35,7 +37,7 @@ const Message = ({ children, loading = false, noMargin = false, type = 'default'
|
||||||
<Card className={cardClasses} body>
|
<Card className={cardClasses} body>
|
||||||
<h3 className={classNames('text-center mb-0', getTextClassForType(type))}>
|
<h3 className={classNames('text-center mb-0', getTextClassForType(type))}>
|
||||||
{loading && <FontAwesomeIcon icon={preloader} spin />}
|
{loading && <FontAwesomeIcon icon={preloader} spin />}
|
||||||
{loading && <span className="ml-2">{children || 'Loading...'}</span>}
|
{loading && <span className="ml-2">{children ?? 'Loading...'}</span>}
|
||||||
{!loading && children}
|
{!loading && children}
|
||||||
</h3>
|
</h3>
|
||||||
</Card>
|
</Card>
|
||||||
|
@ -43,6 +45,4 @@ const Message = ({ children, loading = false, noMargin = false, type = 'default'
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
Message.propTypes = propTypes;
|
|
||||||
|
|
||||||
export default Message;
|
export default Message;
|
|
@ -1,15 +1,14 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { DropdownItem, DropdownMenu, DropdownToggle, UncontrolledDropdown } from 'reactstrap';
|
import { DropdownItem, DropdownMenu, DropdownToggle, UncontrolledDropdown } from 'reactstrap';
|
||||||
import * as PropTypes from 'prop-types';
|
|
||||||
|
|
||||||
const propTypes = {
|
interface PaginationDropdownProps {
|
||||||
toggleClassName: PropTypes.string,
|
ranges: number[];
|
||||||
ranges: PropTypes.arrayOf(PropTypes.number).isRequired,
|
value: number;
|
||||||
value: PropTypes.number.isRequired,
|
setValue: (newValue: number) => void;
|
||||||
setValue: PropTypes.func.isRequired,
|
toggleClassName?: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
const PaginationDropdown = ({ toggleClassName, ranges, value, setValue }) => (
|
const PaginationDropdown = ({ toggleClassName, ranges, value, setValue }: PaginationDropdownProps) => (
|
||||||
<UncontrolledDropdown>
|
<UncontrolledDropdown>
|
||||||
<DropdownToggle caret color="link" className={toggleClassName}>
|
<DropdownToggle caret color="link" className={toggleClassName}>
|
||||||
Paginate
|
Paginate
|
||||||
|
@ -28,6 +27,4 @@ const PaginationDropdown = ({ toggleClassName, ranges, value, setValue }) => (
|
||||||
</UncontrolledDropdown>
|
</UncontrolledDropdown>
|
||||||
);
|
);
|
||||||
|
|
||||||
PaginationDropdown.propTypes = propTypes;
|
|
||||||
|
|
||||||
export default PaginationDropdown;
|
export default PaginationDropdown;
|
|
@ -1,29 +1,30 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import { faSearch as searchIcon } from '@fortawesome/free-solid-svg-icons';
|
import { faSearch as searchIcon } from '@fortawesome/free-solid-svg-icons';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import './SearchField.scss';
|
import './SearchField.scss';
|
||||||
|
|
||||||
const DEFAULT_SEARCH_INTERVAL = 500;
|
const DEFAULT_SEARCH_INTERVAL = 500;
|
||||||
let timer;
|
let timer: NodeJS.Timeout | null;
|
||||||
|
|
||||||
const propTypes = {
|
interface SearchField {
|
||||||
onChange: PropTypes.func.isRequired,
|
onChange: (value: string) => void;
|
||||||
className: PropTypes.string,
|
className?: string;
|
||||||
placeholder: PropTypes.string,
|
placeholder?: string;
|
||||||
large: PropTypes.bool,
|
large?: boolean;
|
||||||
noBorder: PropTypes.bool,
|
noBorder?: boolean;
|
||||||
};
|
}
|
||||||
|
|
||||||
const SearchField = ({ onChange, className, placeholder = 'Search...', large = true, noBorder = false }) => {
|
const SearchField = (
|
||||||
|
{ onChange, className, placeholder = 'Search...', large = true, noBorder = false }: SearchField,
|
||||||
|
) => {
|
||||||
const [ searchTerm, setSearchTerm ] = useState('');
|
const [ searchTerm, setSearchTerm ] = useState('');
|
||||||
|
|
||||||
const resetTimer = () => {
|
const resetTimer = () => {
|
||||||
clearTimeout(timer);
|
timer && clearTimeout(timer);
|
||||||
timer = null;
|
timer = null;
|
||||||
};
|
};
|
||||||
const searchTermChanged = (newSearchTerm, timeout = DEFAULT_SEARCH_INTERVAL) => {
|
const searchTermChanged = (newSearchTerm: string, timeout = DEFAULT_SEARCH_INTERVAL) => {
|
||||||
setSearchTerm(newSearchTerm);
|
setSearchTerm(newSearchTerm);
|
||||||
|
|
||||||
resetTimer();
|
resetTimer();
|
||||||
|
@ -59,6 +60,4 @@ const SearchField = ({ onChange, className, placeholder = 'Search...', large = t
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
SearchField.propTypes = propTypes;
|
|
||||||
|
|
||||||
export default SearchField;
|
export default SearchField;
|
|
@ -1,28 +1,25 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { UncontrolledDropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap';
|
import { UncontrolledDropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap';
|
||||||
import { toPairs } from 'ramda';
|
import { toPairs } from 'ramda';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import { faSortAmountUp as sortAscIcon, faSortAmountDown as sortDescIcon } from '@fortawesome/free-solid-svg-icons';
|
import { faSortAmountUp as sortAscIcon, faSortAmountDown as sortDescIcon } from '@fortawesome/free-solid-svg-icons';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { determineOrderDir } from './utils';
|
import { determineOrderDir, OrderDir } from './utils';
|
||||||
import './SortingDropdown.scss';
|
import './SortingDropdown.scss';
|
||||||
|
|
||||||
const propTypes = {
|
export interface SortingDropdownProps<T extends string = string> {
|
||||||
items: PropTypes.object.isRequired,
|
items: Record<T, string>;
|
||||||
orderField: PropTypes.string,
|
orderField?: T;
|
||||||
orderDir: PropTypes.oneOf([ 'ASC', 'DESC' ]),
|
orderDir?: OrderDir;
|
||||||
onChange: PropTypes.func.isRequired,
|
onChange: (orderField?: T, orderDir?: OrderDir) => void;
|
||||||
isButton: PropTypes.bool,
|
isButton?: boolean;
|
||||||
right: PropTypes.bool,
|
right?: boolean;
|
||||||
};
|
}
|
||||||
const defaultProps = {
|
|
||||||
isButton: true,
|
|
||||||
right: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
const SortingDropdown = ({ items, orderField, orderDir, onChange, isButton, right }) => {
|
export default function SortingDropdown<T extends string = string>(
|
||||||
const handleItemClick = (fieldKey) => () => {
|
{ items, orderField, orderDir, onChange, isButton = true, right = false }: SortingDropdownProps<T>,
|
||||||
|
) {
|
||||||
|
const handleItemClick = (fieldKey: T) => () => {
|
||||||
const newOrderDir = determineOrderDir(fieldKey, orderField, orderDir);
|
const newOrderDir = determineOrderDir(fieldKey, orderField, orderDir);
|
||||||
|
|
||||||
onChange(newOrderDir ? fieldKey : undefined, newOrderDir);
|
onChange(newOrderDir ? fieldKey : undefined, newOrderDir);
|
||||||
|
@ -42,7 +39,7 @@ const SortingDropdown = ({ items, orderField, orderDir, onChange, isButton, righ
|
||||||
className={classNames('sorting-dropdown__menu', { 'sorting-dropdown__menu--link': !isButton })}
|
className={classNames('sorting-dropdown__menu', { 'sorting-dropdown__menu--link': !isButton })}
|
||||||
>
|
>
|
||||||
{toPairs(items).map(([ fieldKey, fieldValue ]) => (
|
{toPairs(items).map(([ fieldKey, fieldValue ]) => (
|
||||||
<DropdownItem key={fieldKey} active={orderField === fieldKey} onClick={handleItemClick(fieldKey)}>
|
<DropdownItem key={fieldKey} active={orderField === fieldKey} onClick={handleItemClick(fieldKey as T)}>
|
||||||
{fieldValue}
|
{fieldValue}
|
||||||
{orderField === fieldKey && (
|
{orderField === fieldKey && (
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
|
@ -59,9 +56,4 @@ const SortingDropdown = ({ items, orderField, orderDir, onChange, isButton, righ
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</UncontrolledDropdown>
|
</UncontrolledDropdown>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
SortingDropdown.propTypes = propTypes;
|
|
||||||
SortingDropdown.defaultProps = defaultProps;
|
|
||||||
|
|
||||||
export default SortingDropdown;
|
|
|
@ -3,7 +3,11 @@ import { SyntheticEvent } from 'react';
|
||||||
|
|
||||||
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 = <T extends string = string>(
|
||||||
|
currentField: T,
|
||||||
|
newField?: T,
|
||||||
|
currentOrderDir?: OrderDir,
|
||||||
|
): OrderDir => {
|
||||||
if (currentField !== newField) {
|
if (currentField !== newField) {
|
||||||
return 'ASC';
|
return 'ASC';
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { shallow } from 'enzyme';
|
import { shallow, ShallowWrapper } from 'enzyme';
|
||||||
import DateRangeRow from '../../src/utils/DateRangeRow';
|
import DateRangeRow from '../../src/utils/DateRangeRow';
|
||||||
import DateInput from '../../src/utils/DateInput';
|
import DateInput from '../../src/utils/DateInput';
|
||||||
|
|
||||||
describe('<DateRangeRow />', () => {
|
describe('<DateRangeRow />', () => {
|
||||||
let wrapper;
|
let wrapper: ShallowWrapper;
|
||||||
const onEndDateChange = jest.fn();
|
const onEndDateChange = jest.fn();
|
||||||
const onStartDateChange = jest.fn();
|
const onStartDateChange = jest.fn();
|
||||||
|
|
|
@ -1,25 +1,25 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { shallow } from 'enzyme';
|
import { shallow, ShallowWrapper } from 'enzyme';
|
||||||
import { DropdownItem } from 'reactstrap';
|
import { DropdownItem } from 'reactstrap';
|
||||||
import { identity, values } from 'ramda';
|
import { identity, values } from 'ramda';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import { faSortAmountDown as caretDownIcon } from '@fortawesome/free-solid-svg-icons';
|
import { faSortAmountDown as caretDownIcon } from '@fortawesome/free-solid-svg-icons';
|
||||||
import SortingDropdown from '../../src/utils/SortingDropdown';
|
import SortingDropdown, { SortingDropdownProps } from '../../src/utils/SortingDropdown';
|
||||||
|
|
||||||
describe('<SortingDropdown />', () => {
|
describe('<SortingDropdown />', () => {
|
||||||
let wrapper;
|
let wrapper: ShallowWrapper;
|
||||||
const items = {
|
const items = {
|
||||||
foo: 'Foo',
|
foo: 'Foo',
|
||||||
bar: 'Bar',
|
bar: 'Bar',
|
||||||
baz: 'Hello World',
|
baz: 'Hello World',
|
||||||
};
|
};
|
||||||
const createWrapper = (props) => {
|
const createWrapper = (props: Partial<SortingDropdownProps> = {}) => {
|
||||||
wrapper = shallow(<SortingDropdown items={items} onChange={identity} {...props} />);
|
wrapper = shallow(<SortingDropdown items={items} onChange={identity} {...props} />);
|
||||||
|
|
||||||
return wrapper;
|
return wrapper;
|
||||||
};
|
};
|
||||||
|
|
||||||
afterEach(() => wrapper && wrapper.unmount());
|
afterEach(() => wrapper?.unmount());
|
||||||
|
|
||||||
it('properly renders provided list of items', () => {
|
it('properly renders provided list of items', () => {
|
||||||
const wrapper = createWrapper();
|
const wrapper = createWrapper();
|
Loading…
Reference in a new issue