mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2024-12-23 09:30:31 +03:00
commit
706e00ace0
21 changed files with 114 additions and 111 deletions
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
|
@ -11,6 +11,6 @@ jobs:
|
|||
ci:
|
||||
uses: shlinkio/github-actions/.github/workflows/web-app-ci.yml@main
|
||||
with:
|
||||
node-version: 18.12
|
||||
node-version: 20.2
|
||||
publish-coverage: true
|
||||
force-install: true
|
||||
|
|
6
.github/workflows/deploy-preview.yml
vendored
6
.github/workflows/deploy-preview.yml
vendored
|
@ -9,14 +9,14 @@ jobs:
|
|||
continue-on-error: true
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
||||
ref: ${{ github.event.pull_request.head.ref }}
|
||||
- name: Use node.js
|
||||
uses: actions/setup-node@v1
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18.12
|
||||
node-version: 20.2
|
||||
- name: Build
|
||||
run: |
|
||||
npm ci --force && \
|
||||
|
|
6
.github/workflows/publish-release.yml
vendored
6
.github/workflows/publish-release.yml
vendored
|
@ -10,11 +10,11 @@ jobs:
|
|||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
- name: Use node.js
|
||||
uses: actions/setup-node@v1
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18.12
|
||||
node-version: 20.2
|
||||
- name: Generate release assets
|
||||
run: npm ci --force && VERSION=${GITHUB_REF#refs/tags/v} npm run build:dist
|
||||
- name: Publish release with assets
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
FROM node:18.12-alpine as node
|
||||
FROM node:20.2-alpine as node
|
||||
COPY . /shlink-web-client
|
||||
ARG VERSION="latest"
|
||||
ENV VERSION ${VERSION}
|
||||
|
|
|
@ -3,7 +3,7 @@ version: '3'
|
|||
services:
|
||||
shlink_web_client_node:
|
||||
container_name: shlink_web_client_node
|
||||
image: node:18.12-alpine
|
||||
image: node:20.2-alpine
|
||||
command: /bin/sh -c "cd /home/shlink/www && npm install --force && npm run start"
|
||||
volumes:
|
||||
- ./:/home/shlink/www
|
||||
|
|
|
@ -5,9 +5,9 @@ import { Link } from 'react-router-dom';
|
|||
import { DropdownItem } from 'reactstrap';
|
||||
import type { SelectedServer } from '../../servers/data';
|
||||
import { getServerId } from '../../servers/data';
|
||||
import { DropdownBtnMenu } from '../../utils/DropdownBtnMenu';
|
||||
import { useFeature } from '../../utils/helpers/features';
|
||||
import { useToggle } from '../../utils/helpers/hooks';
|
||||
import { RowDropdownBtn } from '../../utils/RowDropdownBtn';
|
||||
import { DEFAULT_DOMAIN } from '../../visits/reducers/domainVisits';
|
||||
import type { Domain } from '../data';
|
||||
import type { EditDomainRedirects } from '../reducers/domainRedirects';
|
||||
|
@ -20,7 +20,6 @@ interface DomainDropdownProps {
|
|||
}
|
||||
|
||||
export const DomainDropdown: FC<DomainDropdownProps> = ({ domain, editDomainRedirects, selectedServer }) => {
|
||||
const [isOpen, toggle] = useToggle();
|
||||
const [isModalOpen, toggleModal] = useToggle();
|
||||
const { isDefault } = domain;
|
||||
const canBeEdited = !isDefault || useFeature('defaultDomainRedirectsEdition', selectedServer);
|
||||
|
@ -28,7 +27,7 @@ export const DomainDropdown: FC<DomainDropdownProps> = ({ domain, editDomainRedi
|
|||
const serverId = getServerId(selectedServer);
|
||||
|
||||
return (
|
||||
<DropdownBtnMenu isOpen={isOpen} toggle={toggle}>
|
||||
<RowDropdownBtn>
|
||||
{withVisits && (
|
||||
<DropdownItem
|
||||
tag={Link}
|
||||
|
@ -47,6 +46,6 @@ export const DomainDropdown: FC<DomainDropdownProps> = ({ domain, editDomainRedi
|
|||
toggle={toggleModal}
|
||||
editDomainRedirects={editDomainRedirects}
|
||||
/>
|
||||
</DropdownBtnMenu>
|
||||
</RowDropdownBtn>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -9,8 +9,8 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|||
import type { FC } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { DropdownItem } from 'reactstrap';
|
||||
import { DropdownBtnMenu } from '../utils/DropdownBtnMenu';
|
||||
import { useToggle } from '../utils/helpers/hooks';
|
||||
import { RowDropdownBtn } from '../utils/RowDropdownBtn';
|
||||
import type { ServerWithId } from './data';
|
||||
import type { DeleteServerModalProps } from './DeleteServerModal';
|
||||
|
||||
|
@ -25,14 +25,13 @@ interface ManageServersRowDropdownConnectProps extends ManageServersRowDropdownP
|
|||
export const ManageServersRowDropdown = (
|
||||
DeleteServerModal: FC<DeleteServerModalProps>,
|
||||
): FC<ManageServersRowDropdownConnectProps> => ({ server, setAutoConnect }) => {
|
||||
const [isMenuOpen, toggleMenu] = useToggle();
|
||||
const [isModalOpen,, showModal, hideModal] = useToggle();
|
||||
const serverUrl = `/server/${server.id}`;
|
||||
const { autoConnect: isAutoConnect } = server;
|
||||
const autoConnectIcon = isAutoConnect ? toggleOffIcon : toggleOnIcon;
|
||||
|
||||
return (
|
||||
<DropdownBtnMenu isOpen={isMenuOpen} toggle={toggleMenu}>
|
||||
<RowDropdownBtn minWidth={170}>
|
||||
<DropdownItem tag={Link} to={serverUrl}>
|
||||
<FontAwesomeIcon icon={connectIcon} fixedWidth /> Connect
|
||||
</DropdownItem>
|
||||
|
@ -48,6 +47,6 @@ export const ManageServersRowDropdown = (
|
|||
</DropdownItem>
|
||||
|
||||
<DeleteServerModal redirectHome={false} server={server} isOpen={isModalOpen} toggle={hideModal} />
|
||||
</DropdownBtnMenu>
|
||||
</RowDropdownBtn>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -17,7 +17,7 @@ export const ShortUrlsFilterDropdown = (
|
|||
const onFilterClick = (key: keyof ShortUrlsFilter) => () => onChange({ ...selected, [key]: !selected?.[key] });
|
||||
|
||||
return (
|
||||
<DropdownBtn text="Filters" dropdownClassName={className} className="me-3" right minWidth={250}>
|
||||
<DropdownBtn text="Filters" dropdownClassName={className} inline end minWidth={250}>
|
||||
<DropdownItem header>Visits:</DropdownItem>
|
||||
<DropdownItem active={excludeBots} onClick={onFilterClick('excludeBots')}>Ignore visits from bots</DropdownItem>
|
||||
|
||||
|
|
|
@ -87,7 +87,7 @@ export const ShortUrlsRow = (
|
|||
<td className="responsive-table__cell short-urls-row__cell" data-th="Status">
|
||||
<ShortUrlStatus shortUrl={shortUrl} />
|
||||
</td>
|
||||
<td className="responsive-table__cell short-urls-row__cell">
|
||||
<td className="responsive-table__cell short-urls-row__cell text-end">
|
||||
<ShortUrlsRowMenu selectedServer={selectedServer} shortUrl={shortUrl} />
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -8,8 +8,8 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|||
import type { FC } from 'react';
|
||||
import { DropdownItem } from 'reactstrap';
|
||||
import type { SelectedServer } from '../../servers/data';
|
||||
import { DropdownBtnMenu } from '../../utils/DropdownBtnMenu';
|
||||
import { useToggle } from '../../utils/helpers/hooks';
|
||||
import { RowDropdownBtn } from '../../utils/RowDropdownBtn';
|
||||
import type { ShortUrl, ShortUrlModalProps } from '../data';
|
||||
import { ShortUrlDetailLink } from './ShortUrlDetailLink';
|
||||
|
||||
|
@ -23,12 +23,11 @@ export const ShortUrlsRowMenu = (
|
|||
DeleteShortUrlModal: ShortUrlModal,
|
||||
QrCodeModal: ShortUrlModal,
|
||||
) => ({ shortUrl, selectedServer }: ShortUrlsRowMenuProps) => {
|
||||
const [isOpen, toggle] = useToggle();
|
||||
const [isQrModalOpen,, openQrCodeModal, closeQrCodeModal] = useToggle();
|
||||
const [isDeleteModalOpen,, openDeleteModal, closeDeleteModal] = useToggle();
|
||||
|
||||
return (
|
||||
<DropdownBtnMenu toggle={toggle} isOpen={isOpen}>
|
||||
<RowDropdownBtn minWidth={190}>
|
||||
<DropdownItem tag={ShortUrlDetailLink} selectedServer={selectedServer} shortUrl={shortUrl} suffix="visits">
|
||||
<FontAwesomeIcon icon={pieChartIcon} fixedWidth /> Visit stats
|
||||
</DropdownItem>
|
||||
|
@ -48,7 +47,7 @@ export const ShortUrlsRowMenu = (
|
|||
<FontAwesomeIcon icon={deleteIcon} fixedWidth /> Delete short URL
|
||||
</DropdownItem>
|
||||
<DeleteShortUrlModal shortUrl={shortUrl} isOpen={isDeleteModalOpen} toggle={closeDeleteModal} />
|
||||
</DropdownBtnMenu>
|
||||
</RowDropdownBtn>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -5,9 +5,9 @@ import { Link } from 'react-router-dom';
|
|||
import { DropdownItem } from 'reactstrap';
|
||||
import type { SelectedServer } from '../servers/data';
|
||||
import { getServerId } from '../servers/data';
|
||||
import { DropdownBtnMenu } from '../utils/DropdownBtnMenu';
|
||||
import { useToggle } from '../utils/helpers/hooks';
|
||||
import { prettify } from '../utils/helpers/numbers';
|
||||
import { RowDropdownBtn } from '../utils/RowDropdownBtn';
|
||||
import type { ColorGenerator } from '../utils/services/ColorGenerator';
|
||||
import type { SimplifiedTag, TagModalProps } from './data';
|
||||
import { TagBullet } from './helpers/TagBullet';
|
||||
|
@ -24,7 +24,6 @@ export const TagsTableRow = (
|
|||
) => ({ tag, selectedServer }: TagsTableRowProps) => {
|
||||
const [isDeleteModalOpen, toggleDelete] = useToggle();
|
||||
const [isEditModalOpen, toggleEdit] = useToggle();
|
||||
const [isDropdownOpen, toggleDropdown] = useToggle();
|
||||
const serverId = getServerId(selectedServer);
|
||||
|
||||
return (
|
||||
|
@ -43,14 +42,14 @@ export const TagsTableRow = (
|
|||
</Link>
|
||||
</td>
|
||||
<td className="responsive-table__cell text-lg-end">
|
||||
<DropdownBtnMenu toggle={toggleDropdown} isOpen={isDropdownOpen}>
|
||||
<RowDropdownBtn>
|
||||
<DropdownItem onClick={toggleEdit}>
|
||||
<FontAwesomeIcon icon={editIcon} fixedWidth className="me-1" /> Edit
|
||||
</DropdownItem>
|
||||
<DropdownItem onClick={toggleDelete}>
|
||||
<FontAwesomeIcon icon={deleteIcon} fixedWidth className="me-1" /> Delete
|
||||
</DropdownItem>
|
||||
</DropdownBtnMenu>
|
||||
</RowDropdownBtn>
|
||||
</td>
|
||||
|
||||
<EditTagModal tag={tag.tag} toggle={toggleEdit} isOpen={isEditModalOpen} />
|
||||
|
|
|
@ -2,13 +2,20 @@
|
|||
|
||||
@import '../utils/mixins/vertical-align';
|
||||
|
||||
.dropdown-btn__toggle.dropdown-btn__toggle {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.dropdown-btn__toggle.dropdown-btn__toggle--with-caret {
|
||||
padding-right: 1.75rem;
|
||||
}
|
||||
|
||||
.dropdown-btn__toggle.dropdown-btn__toggle,
|
||||
.dropdown-btn__toggle.dropdown-btn__toggle:not(:disabled):not(.disabled).active,
|
||||
.dropdown-btn__toggle.dropdown-btn__toggle:not(:disabled):not(.disabled):active,
|
||||
.dropdown-btn__toggle.dropdown-btn__toggle:not(:disabled):not(.disabled):focus,
|
||||
.dropdown-btn__toggle.dropdown-btn__toggle:not(:disabled):not(.disabled):hover,
|
||||
.show > .dropdown-btn__toggle.dropdown-btn__toggle.dropdown-toggle {
|
||||
text-align: left;
|
||||
color: var(--input-text-color);
|
||||
background-color: var(--primary-color);
|
||||
border-color: var(--input-border-color);
|
||||
|
|
|
@ -1,28 +1,45 @@
|
|||
import type { FC, PropsWithChildren } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import type { FC, PropsWithChildren, ReactNode } from 'react';
|
||||
import { Dropdown, DropdownMenu, DropdownToggle } from 'reactstrap';
|
||||
import type { DropdownToggleProps } from 'reactstrap/types/lib/DropdownToggle';
|
||||
import { useToggle } from './helpers/hooks';
|
||||
import './DropdownBtn.scss';
|
||||
|
||||
export type DropdownBtnProps = PropsWithChildren<{
|
||||
text: string;
|
||||
disabled?: boolean;
|
||||
export type DropdownBtnProps = PropsWithChildren<Omit<DropdownToggleProps, 'caret' | 'size' | 'outline'> & {
|
||||
text: ReactNode;
|
||||
noCaret?: boolean;
|
||||
className?: string;
|
||||
dropdownClassName?: string;
|
||||
right?: boolean;
|
||||
inline?: boolean;
|
||||
minWidth?: number;
|
||||
size?: 'sm' | 'md' | 'lg';
|
||||
}>;
|
||||
|
||||
export const DropdownBtn: FC<DropdownBtnProps> = (
|
||||
{ text, disabled = false, className = '', children, dropdownClassName, right = false, minWidth },
|
||||
) => {
|
||||
export const DropdownBtn: FC<DropdownBtnProps> = ({
|
||||
text,
|
||||
disabled = false,
|
||||
className,
|
||||
children,
|
||||
dropdownClassName,
|
||||
noCaret,
|
||||
end = false,
|
||||
minWidth,
|
||||
inline,
|
||||
size,
|
||||
}) => {
|
||||
const [isOpen, toggle] = useToggle();
|
||||
const toggleClasses = `dropdown-btn__toggle btn-block ${className}`;
|
||||
const style = { minWidth: minWidth && `${minWidth}px` };
|
||||
const toggleClasses = classNames('dropdown-btn__toggle', className, {
|
||||
'btn-block': !inline,
|
||||
'dropdown-btn__toggle--with-caret': !noCaret,
|
||||
});
|
||||
const menuStyle = { minWidth: minWidth && `${minWidth}px` };
|
||||
|
||||
return (
|
||||
<Dropdown isOpen={isOpen} toggle={toggle} disabled={disabled} className={dropdownClassName}>
|
||||
<DropdownToggle caret className={toggleClasses} color="primary">{text}</DropdownToggle>
|
||||
<DropdownMenu className="w-100" end={right} style={style}>{children}</DropdownMenu>
|
||||
<DropdownToggle size={size} caret={!noCaret} className={toggleClasses} color="primary">
|
||||
{text}
|
||||
</DropdownToggle>
|
||||
<DropdownMenu className="w-100" end={end} style={menuStyle}>{children}</DropdownMenu>
|
||||
</Dropdown>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
.dropdown-btn-menu__dropdown-toggle:after {
|
||||
display: none !important;
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
import { faEllipsisV as menuIcon } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import type { FC, PropsWithChildren } from 'react';
|
||||
import { ButtonDropdown, DropdownMenu, DropdownToggle } from 'reactstrap';
|
||||
import './DropdownBtnMenu.scss';
|
||||
|
||||
export type DropdownBtnMenuProps = PropsWithChildren<{
|
||||
isOpen: boolean;
|
||||
toggle: () => void;
|
||||
right?: boolean;
|
||||
}>;
|
||||
|
||||
export const DropdownBtnMenu: FC<DropdownBtnMenuProps> = ({ isOpen, toggle, children, right = true }) => (
|
||||
<ButtonDropdown toggle={toggle} isOpen={isOpen}>
|
||||
<DropdownToggle size="sm" caret outline className="dropdown-btn-menu__dropdown-toggle">
|
||||
<FontAwesomeIcon icon={menuIcon} />
|
||||
</DropdownToggle>
|
||||
<DropdownMenu end={right}>{children}</DropdownMenu>
|
||||
</ButtonDropdown>
|
||||
);
|
|
@ -5,10 +5,10 @@ import type { ButtonProps } from 'reactstrap';
|
|||
import { Button } from 'reactstrap';
|
||||
import { prettify } from './helpers/numbers';
|
||||
|
||||
interface ExportBtnProps extends Omit<ButtonProps, 'outline' | 'color' | 'disabled'> {
|
||||
type ExportBtnProps = Omit<ButtonProps, 'outline' | 'color' | 'disabled'> & {
|
||||
amount?: number;
|
||||
loading?: boolean;
|
||||
}
|
||||
};
|
||||
|
||||
export const ExportBtn: FC<ExportBtnProps> = ({ amount = 0, loading = false, ...rest }) => (
|
||||
<Button {...rest} outline color="primary" disabled={loading}>
|
||||
|
|
21
src/utils/RowDropdownBtn.tsx
Normal file
21
src/utils/RowDropdownBtn.tsx
Normal file
|
@ -0,0 +1,21 @@
|
|||
import { faEllipsisV as menuIcon } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import type { FC, PropsWithChildren } from 'react';
|
||||
import { DropdownBtn } from './DropdownBtn';
|
||||
|
||||
export type DropdownBtnMenuProps = PropsWithChildren<{
|
||||
minWidth?: number;
|
||||
}>;
|
||||
|
||||
export const RowDropdownBtn: FC<DropdownBtnMenuProps> = ({ children, minWidth }) => (
|
||||
<DropdownBtn
|
||||
text={<FontAwesomeIcon className="px-1" icon={menuIcon} />}
|
||||
size="sm"
|
||||
minWidth={minWidth}
|
||||
end
|
||||
noCaret
|
||||
inline
|
||||
>
|
||||
{children}
|
||||
</DropdownBtn>
|
||||
);
|
|
@ -22,7 +22,7 @@ export const VisitsFilterDropdown = (
|
|||
const onBotsClick = () => onChange({ ...selected, excludeBots: !selected?.excludeBots });
|
||||
|
||||
return (
|
||||
<DropdownBtn text="Filters" dropdownClassName={className} className="me-3" right minWidth={250}>
|
||||
<DropdownBtn text="Filters" dropdownClassName={className} inline end minWidth={250}>
|
||||
<DropdownItem header>Bots:</DropdownItem>
|
||||
<DropdownItem active={excludeBots} onClick={onBotsClick}>Exclude potential bots</DropdownItem>
|
||||
|
||||
|
|
|
@ -26,9 +26,8 @@ describe('<DomainSelector />', () => {
|
|||
const btn = screen.getByRole('button', { name: expectedText });
|
||||
|
||||
expect(screen.queryByPlaceholderText('Domain')).not.toBeInTheDocument();
|
||||
expect(btn).toHaveAttribute(
|
||||
'class',
|
||||
`dropdown-btn__toggle btn-block ${expectedClassName} dropdown-toggle btn btn-primary`,
|
||||
expect(btn).toHaveClass(
|
||||
`dropdown-btn__toggle ${expectedClassName} btn-block dropdown-btn__toggle--with-caret dropdown-toggle btn btn-primary`,
|
||||
);
|
||||
await user.click(btn);
|
||||
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
import { screen } from '@testing-library/react';
|
||||
import { fromPartial } from '@total-typescript/shoehorn';
|
||||
import type { DropdownBtnMenuProps } from '../../src/utils/DropdownBtnMenu';
|
||||
import { DropdownBtnMenu } from '../../src/utils/DropdownBtnMenu';
|
||||
import { renderWithEvents } from '../__helpers__/setUpTest';
|
||||
|
||||
describe('<DropdownBtnMenu />', () => {
|
||||
const setUp = (props: Partial<DropdownBtnMenuProps> = {}) => renderWithEvents(
|
||||
<DropdownBtnMenu {...fromPartial<DropdownBtnMenuProps>({ toggle: jest.fn(), ...props })}>
|
||||
the children
|
||||
</DropdownBtnMenu>,
|
||||
);
|
||||
|
||||
it('renders expected components', () => {
|
||||
setUp();
|
||||
const toggle = screen.getByRole('button');
|
||||
|
||||
expect(toggle).toBeInTheDocument();
|
||||
expect(toggle).toHaveClass('btn-sm');
|
||||
expect(toggle).toHaveClass('dropdown-btn-menu__dropdown-toggle');
|
||||
expect(screen.getByRole('img', { hidden: true })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders expected children', () => {
|
||||
setUp();
|
||||
expect(screen.getByText('the children')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it.each([
|
||||
[undefined, true],
|
||||
[true, true],
|
||||
[false, false],
|
||||
])('renders menu to the end when expected', (right, expectedEnd) => {
|
||||
setUp({ right });
|
||||
|
||||
if (expectedEnd) {
|
||||
expect(screen.getByRole('menu', { hidden: true })).toHaveClass('dropdown-menu-end');
|
||||
} else {
|
||||
expect(screen.getByRole('menu', { hidden: true })).not.toHaveClass('dropdown-menu-end');
|
||||
}
|
||||
});
|
||||
});
|
28
test/utils/RowDropdownBtn.test.tsx
Normal file
28
test/utils/RowDropdownBtn.test.tsx
Normal file
|
@ -0,0 +1,28 @@
|
|||
import { screen } from '@testing-library/react';
|
||||
import { fromPartial } from '@total-typescript/shoehorn';
|
||||
import type { DropdownBtnMenuProps } from '../../src/utils/RowDropdownBtn';
|
||||
import { RowDropdownBtn } from '../../src/utils/RowDropdownBtn';
|
||||
import { renderWithEvents } from '../__helpers__/setUpTest';
|
||||
|
||||
describe('<RowDropdownBtn />', () => {
|
||||
const setUp = (props: Partial<DropdownBtnMenuProps> = {}) => renderWithEvents(
|
||||
<RowDropdownBtn {...fromPartial<DropdownBtnMenuProps>({ ...props })}>
|
||||
the children
|
||||
</RowDropdownBtn>,
|
||||
);
|
||||
|
||||
it('renders expected components', () => {
|
||||
setUp();
|
||||
const toggle = screen.getByRole('button');
|
||||
|
||||
expect(toggle).toBeInTheDocument();
|
||||
expect(toggle).toHaveClass('btn-sm');
|
||||
expect(toggle).toHaveClass('dropdown-btn__toggle');
|
||||
expect(screen.getByRole('img', { hidden: true })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders expected children', () => {
|
||||
setUp();
|
||||
expect(screen.getByText('the children')).toBeInTheDocument();
|
||||
});
|
||||
});
|
Loading…
Reference in a new issue