mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2025-12-11 10:13:55 -06:00
Replace reactstrap nav bar with tailwind-based one
This commit is contained in:
parent
9c0c2fc3f9
commit
d10d7fd96d
@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
* Update to `@shlinkio/shlink-frontend-kit` 0.9 and `@shlinkio/shlink-web-component` 0.14 to add initial support to the new light theme brand color.
|
* Update to `@shlinkio/shlink-frontend-kit` 0.9 and `@shlinkio/shlink-web-component` 0.14 to add initial support to the new light theme brand color.
|
||||||
|
* Replace reactstrap nav bar with `NavBar` component from `@shlinkio/shlink-frontend-kit`
|
||||||
|
|
||||||
### Deprecated
|
### Deprecated
|
||||||
* *Nothing*
|
* *Nothing*
|
||||||
|
|||||||
50
package-lock.json
generated
50
package-lock.json
generated
@ -16,9 +16,9 @@
|
|||||||
"@json2csv/plainjs": "^7.0.6",
|
"@json2csv/plainjs": "^7.0.6",
|
||||||
"@reduxjs/toolkit": "^2.8.2",
|
"@reduxjs/toolkit": "^2.8.2",
|
||||||
"@shlinkio/data-manipulation": "^1.0.3",
|
"@shlinkio/data-manipulation": "^1.0.3",
|
||||||
"@shlinkio/shlink-frontend-kit": "^0.9.10",
|
"@shlinkio/shlink-frontend-kit": "^0.9.13",
|
||||||
"@shlinkio/shlink-js-sdk": "^2.1.0",
|
"@shlinkio/shlink-js-sdk": "^2.1.0",
|
||||||
"@shlinkio/shlink-web-component": "^0.14.2",
|
"@shlinkio/shlink-web-component": "^0.14.3",
|
||||||
"bootstrap": "5.2.3",
|
"bootstrap": "5.2.3",
|
||||||
"bottlejs": "^2.0.1",
|
"bottlejs": "^2.0.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
@ -3516,9 +3516,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@shlinkio/shlink-frontend-kit": {
|
"node_modules/@shlinkio/shlink-frontend-kit": {
|
||||||
"version": "0.9.10",
|
"version": "0.9.13",
|
||||||
"resolved": "https://registry.npmjs.org/@shlinkio/shlink-frontend-kit/-/shlink-frontend-kit-0.9.10.tgz",
|
"resolved": "https://registry.npmjs.org/@shlinkio/shlink-frontend-kit/-/shlink-frontend-kit-0.9.13.tgz",
|
||||||
"integrity": "sha512-L1+z3imvoSXHYWaO+H39JXGg40eQW1ytY3hMIE8JUuqJYNmWWLrafmfj1MHenCWGZEhymbQnpGD1yyziy6a9Lw==",
|
"integrity": "sha512-qiEDmrzYA/rmHPdLI9znaYqMKD16ITWon/vG66LlUeDL3zR0Psppy79FjM5k3CmIGsCCZdekXQle6U19YSOQLA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@floating-ui/react": "^0.27.12",
|
"@floating-ui/react": "^0.27.12",
|
||||||
@ -3550,9 +3550,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@shlinkio/shlink-web-component": {
|
"node_modules/@shlinkio/shlink-web-component": {
|
||||||
"version": "0.14.2",
|
"version": "0.14.3",
|
||||||
"resolved": "https://registry.npmjs.org/@shlinkio/shlink-web-component/-/shlink-web-component-0.14.2.tgz",
|
"resolved": "https://registry.npmjs.org/@shlinkio/shlink-web-component/-/shlink-web-component-0.14.3.tgz",
|
||||||
"integrity": "sha512-4GRT1nLuhVCGuKP8fwRv1EBtgQ2wCvpJqJ6ipYM/QKwA2uJIXChom4TDia+s4X8mESIOjV0++aoPOEr6y6H2iA==",
|
"integrity": "sha512-IgTHJYkxp6Pqo4E8waouBXbpytiRasqPyMAMQ5vYfGXU5Y53D4QNkRwPsILkEVTb7B+qwQegyNax4YKjRi/hgA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@formkit/drag-and-drop": "^0.5.3",
|
"@formkit/drag-and-drop": "^0.5.3",
|
||||||
@ -3569,7 +3569,6 @@
|
|||||||
"react-external-link": "^2.5.0",
|
"react-external-link": "^2.5.0",
|
||||||
"react-leaflet": "^4.2.1 || ^5.0",
|
"react-leaflet": "^4.2.1 || ^5.0",
|
||||||
"react-swipeable": "^7.0.2",
|
"react-swipeable": "^7.0.2",
|
||||||
"react-tag-autocomplete": "^7.5.0",
|
|
||||||
"recharts": "^2.15.3"
|
"recharts": "^2.15.3"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
@ -3579,7 +3578,7 @@
|
|||||||
"@fortawesome/free-solid-svg-icons": "^6.7.2",
|
"@fortawesome/free-solid-svg-icons": "^6.7.2",
|
||||||
"@fortawesome/react-fontawesome": "^0.2.2",
|
"@fortawesome/react-fontawesome": "^0.2.2",
|
||||||
"@reduxjs/toolkit": "^2.5.0",
|
"@reduxjs/toolkit": "^2.5.0",
|
||||||
"@shlinkio/shlink-frontend-kit": "^0.9.10",
|
"@shlinkio/shlink-frontend-kit": "^0.9.11",
|
||||||
"@shlinkio/shlink-js-sdk": "^2.0.0",
|
"@shlinkio/shlink-js-sdk": "^2.0.0",
|
||||||
"react": "^18.3 || ^19.0",
|
"react": "^18.3 || ^19.0",
|
||||||
"react-dom": "^18.3 || ^19.0",
|
"react-dom": "^18.3 || ^19.0",
|
||||||
@ -9400,18 +9399,6 @@
|
|||||||
"react": "^16.8.3 || ^17 || ^18 || ^19.0.0 || ^19.0.0-rc"
|
"react": "^16.8.3 || ^17 || ^18 || ^19.0.0 || ^19.0.0-rc"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-tag-autocomplete": {
|
|
||||||
"version": "7.5.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/react-tag-autocomplete/-/react-tag-autocomplete-7.5.0.tgz",
|
|
||||||
"integrity": "sha512-uy6ncusMKr6p3ip7xb4DTYtF22g7cSRyZq0IeFpgmrQipTbKz4RVFDB5QnnqstN6HTs9cdTtIb3vVuOuOdzH3w==",
|
|
||||||
"license": "ISC",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 16.12.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"react": "^18.0.0 || ^19.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/react-transition-group": {
|
"node_modules/react-transition-group": {
|
||||||
"version": "4.4.5",
|
"version": "4.4.5",
|
||||||
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
|
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
|
||||||
@ -14073,9 +14060,9 @@
|
|||||||
"requires": {}
|
"requires": {}
|
||||||
},
|
},
|
||||||
"@shlinkio/shlink-frontend-kit": {
|
"@shlinkio/shlink-frontend-kit": {
|
||||||
"version": "0.9.10",
|
"version": "0.9.13",
|
||||||
"resolved": "https://registry.npmjs.org/@shlinkio/shlink-frontend-kit/-/shlink-frontend-kit-0.9.10.tgz",
|
"resolved": "https://registry.npmjs.org/@shlinkio/shlink-frontend-kit/-/shlink-frontend-kit-0.9.13.tgz",
|
||||||
"integrity": "sha512-L1+z3imvoSXHYWaO+H39JXGg40eQW1ytY3hMIE8JUuqJYNmWWLrafmfj1MHenCWGZEhymbQnpGD1yyziy6a9Lw==",
|
"integrity": "sha512-qiEDmrzYA/rmHPdLI9znaYqMKD16ITWon/vG66LlUeDL3zR0Psppy79FjM5k3CmIGsCCZdekXQle6U19YSOQLA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@floating-ui/react": "^0.27.12",
|
"@floating-ui/react": "^0.27.12",
|
||||||
"clsx": "^2.1.1"
|
"clsx": "^2.1.1"
|
||||||
@ -14087,9 +14074,9 @@
|
|||||||
"integrity": "sha512-K6zmA/A7Ux9hTn+ZjAm85YmMl7/v5XgZBM62syCxCsK7Tdw7Gg4+C06cZ2gUv+HWrHtv5IXsi4ax00++8Kg5vw=="
|
"integrity": "sha512-K6zmA/A7Ux9hTn+ZjAm85YmMl7/v5XgZBM62syCxCsK7Tdw7Gg4+C06cZ2gUv+HWrHtv5IXsi4ax00++8Kg5vw=="
|
||||||
},
|
},
|
||||||
"@shlinkio/shlink-web-component": {
|
"@shlinkio/shlink-web-component": {
|
||||||
"version": "0.14.2",
|
"version": "0.14.3",
|
||||||
"resolved": "https://registry.npmjs.org/@shlinkio/shlink-web-component/-/shlink-web-component-0.14.2.tgz",
|
"resolved": "https://registry.npmjs.org/@shlinkio/shlink-web-component/-/shlink-web-component-0.14.3.tgz",
|
||||||
"integrity": "sha512-4GRT1nLuhVCGuKP8fwRv1EBtgQ2wCvpJqJ6ipYM/QKwA2uJIXChom4TDia+s4X8mESIOjV0++aoPOEr6y6H2iA==",
|
"integrity": "sha512-IgTHJYkxp6Pqo4E8waouBXbpytiRasqPyMAMQ5vYfGXU5Y53D4QNkRwPsILkEVTb7B+qwQegyNax4YKjRi/hgA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@formkit/drag-and-drop": "^0.5.3",
|
"@formkit/drag-and-drop": "^0.5.3",
|
||||||
"@json2csv/plainjs": "^7.0.6",
|
"@json2csv/plainjs": "^7.0.6",
|
||||||
@ -14105,7 +14092,6 @@
|
|||||||
"react-external-link": "^2.5.0",
|
"react-external-link": "^2.5.0",
|
||||||
"react-leaflet": "^4.2.1 || ^5.0",
|
"react-leaflet": "^4.2.1 || ^5.0",
|
||||||
"react-swipeable": "^7.0.2",
|
"react-swipeable": "^7.0.2",
|
||||||
"react-tag-autocomplete": "^7.5.0",
|
|
||||||
"recharts": "^2.15.3"
|
"recharts": "^2.15.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -18014,12 +18000,6 @@
|
|||||||
"integrity": "sha512-v1Qx1l+aC2fdxKa9aKJiaU/ZxmJ5o98RMoFwUqAAzVWUcxgfHFXDDruCKXhw6zIYXm6V64JiHgP9f6mlME5l8w==",
|
"integrity": "sha512-v1Qx1l+aC2fdxKa9aKJiaU/ZxmJ5o98RMoFwUqAAzVWUcxgfHFXDDruCKXhw6zIYXm6V64JiHgP9f6mlME5l8w==",
|
||||||
"requires": {}
|
"requires": {}
|
||||||
},
|
},
|
||||||
"react-tag-autocomplete": {
|
|
||||||
"version": "7.5.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/react-tag-autocomplete/-/react-tag-autocomplete-7.5.0.tgz",
|
|
||||||
"integrity": "sha512-uy6ncusMKr6p3ip7xb4DTYtF22g7cSRyZq0IeFpgmrQipTbKz4RVFDB5QnnqstN6HTs9cdTtIb3vVuOuOdzH3w==",
|
|
||||||
"requires": {}
|
|
||||||
},
|
|
||||||
"react-transition-group": {
|
"react-transition-group": {
|
||||||
"version": "4.4.5",
|
"version": "4.4.5",
|
||||||
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
|
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
|
||||||
|
|||||||
@ -29,9 +29,9 @@
|
|||||||
"@json2csv/plainjs": "^7.0.6",
|
"@json2csv/plainjs": "^7.0.6",
|
||||||
"@reduxjs/toolkit": "^2.8.2",
|
"@reduxjs/toolkit": "^2.8.2",
|
||||||
"@shlinkio/data-manipulation": "^1.0.3",
|
"@shlinkio/data-manipulation": "^1.0.3",
|
||||||
"@shlinkio/shlink-frontend-kit": "^0.9.10",
|
"@shlinkio/shlink-frontend-kit": "^0.9.13",
|
||||||
"@shlinkio/shlink-js-sdk": "^2.1.0",
|
"@shlinkio/shlink-js-sdk": "^2.1.0",
|
||||||
"@shlinkio/shlink-web-component": "^0.14.2",
|
"@shlinkio/shlink-web-component": "^0.14.3",
|
||||||
"bootstrap": "5.2.3",
|
"bootstrap": "5.2.3",
|
||||||
"bottlejs": "^2.0.1",
|
"bottlejs": "^2.0.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
|
|||||||
@ -64,7 +64,7 @@ const App: FCWithDeps<AppProps, AppDeps> = (
|
|||||||
<div className="tw:h-full">
|
<div className="tw:h-full">
|
||||||
<MainHeader />
|
<MainHeader />
|
||||||
|
|
||||||
<div className="tw:h-full tw:pt-(--header-height)">
|
<div className="tw:h-full tw:pt-(--tw-header-height)">
|
||||||
<div
|
<div
|
||||||
data-testid="shlink-wrapper"
|
data-testid="shlink-wrapper"
|
||||||
className={clsx(
|
className={clsx(
|
||||||
|
|||||||
@ -1,11 +1,8 @@
|
|||||||
import { faChevronDown as arrowIcon, faCogs as cogsIcon } from '@fortawesome/free-solid-svg-icons';
|
import { faCogs as cogsIcon } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import { useToggle } from '@shlinkio/shlink-frontend-kit';
|
import { NavBar } from '@shlinkio/shlink-frontend-kit/tailwind';
|
||||||
import { clsx } from 'clsx';
|
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
import { useEffect } from 'react';
|
|
||||||
import { Link, useLocation } from 'react-router';
|
import { Link, useLocation } from 'react-router';
|
||||||
import { Collapse, Nav, Navbar, NavbarBrand, NavbarToggler, NavItem, NavLink } from 'reactstrap';
|
|
||||||
import type { FCWithDeps } from '../container/utils';
|
import type { FCWithDeps } from '../container/utils';
|
||||||
import { componentFactory, useDependencies } from '../container/utils';
|
import { componentFactory, useDependencies } from '../container/utils';
|
||||||
import { ShlinkLogo } from './img/ShlinkLogo';
|
import { ShlinkLogo } from './img/ShlinkLogo';
|
||||||
@ -16,39 +13,28 @@ type MainHeaderDeps = {
|
|||||||
|
|
||||||
const MainHeader: FCWithDeps<unknown, MainHeaderDeps> = () => {
|
const MainHeader: FCWithDeps<unknown, MainHeaderDeps> = () => {
|
||||||
const { ServersDropdown } = useDependencies(MainHeader);
|
const { ServersDropdown } = useDependencies(MainHeader);
|
||||||
const { flag: isNotCollapsed, toggle: toggleCollapse, setToFalse: collapse } = useToggle(false, true);
|
const { pathname } = useLocation();
|
||||||
const location = useLocation();
|
|
||||||
const { pathname } = location;
|
|
||||||
|
|
||||||
// In mobile devices, collapse the navbar when location changes
|
|
||||||
useEffect(collapse, [location, collapse]);
|
|
||||||
|
|
||||||
const settingsPath = '/settings';
|
const settingsPath = '/settings';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Navbar color="primary" dark fixed="top" expand="md" className="tw:text-white tw:bg-lm-main tw:dark:bg-dm-main">
|
<NavBar
|
||||||
<NavbarBrand tag={Link} to="/">
|
className="tw:[&]:fixed tw:top-0 tw:z-900"
|
||||||
<ShlinkLogo className="tw:inline tw:w-7 tw:mr-1" color="white" /> Shlink
|
brand={(
|
||||||
</NavbarBrand>
|
<Link to="/" className="tw:[&]:text-white tw:no-underline tw:flex tw:items-center tw:gap-2">
|
||||||
|
<ShlinkLogo className="tw:w-7" color="white" /> <small className="tw:font-normal">Shlink</small>
|
||||||
<NavbarToggler onClick={toggleCollapse}>
|
</Link>
|
||||||
<FontAwesomeIcon
|
)}
|
||||||
icon={arrowIcon}
|
>
|
||||||
className={clsx('tw:transition-transform tw:duration-300', { 'tw:rotate-180': isNotCollapsed })}
|
<NavBar.MenuItem
|
||||||
/>
|
to={settingsPath}
|
||||||
</NavbarToggler>
|
active={pathname.startsWith(settingsPath)}
|
||||||
|
className="tw:flex tw:items-center tw:gap-1.5"
|
||||||
<Collapse navbar isOpen={isNotCollapsed}>
|
>
|
||||||
<Nav navbar className="tw:ml-auto">
|
<FontAwesomeIcon icon={cogsIcon} /> Settings
|
||||||
<NavItem>
|
</NavBar.MenuItem>
|
||||||
<NavLink tag={Link} to={settingsPath} active={pathname.startsWith(settingsPath)}>
|
|
||||||
<FontAwesomeIcon icon={cogsIcon} /> Settings
|
|
||||||
</NavLink>
|
|
||||||
</NavItem>
|
|
||||||
<ServersDropdown />
|
<ServersDropdown />
|
||||||
</Nav>
|
</NavBar>
|
||||||
</Collapse>
|
|
||||||
</Navbar>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import { faPlus as plusIcon, faServer as serverIcon } from '@fortawesome/free-solid-svg-icons';
|
import { faPlus as plusIcon, faServer as serverIcon } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import { Link } from 'react-router';
|
import { Dropdown, NavBar } from '@shlinkio/shlink-frontend-kit/tailwind';
|
||||||
import { DropdownItem, DropdownMenu, DropdownToggle, UncontrolledDropdown } from 'reactstrap';
|
|
||||||
import type { SelectedServer, ServersMap } from './data';
|
import type { SelectedServer, ServersMap } from './data';
|
||||||
import { getServerId } from './data';
|
import { getServerId } from './data';
|
||||||
|
|
||||||
@ -14,29 +13,28 @@ export const ServersDropdown = ({ servers, selectedServer }: ServersDropdownProp
|
|||||||
const serversList = Object.values(servers);
|
const serversList = Object.values(servers);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<UncontrolledDropdown nav inNavbar>
|
<NavBar.Dropdown buttonContent={(
|
||||||
<DropdownToggle nav caret>
|
<span className="tw:flex tw:items-center tw:gap-1.5">
|
||||||
<FontAwesomeIcon icon={serverIcon} /> <span className="tw:ml-1">Servers</span>
|
<FontAwesomeIcon icon={serverIcon} fixedWidth /> Servers
|
||||||
</DropdownToggle>
|
</span>
|
||||||
<DropdownMenu end className="tw:right-0">
|
)}>
|
||||||
{serversList.length === 0 ? (
|
{serversList.length === 0 ? (
|
||||||
<DropdownItem tag={Link} to="/server/create">
|
<Dropdown.Item to="/server/create">
|
||||||
<FontAwesomeIcon icon={plusIcon} /> <span className="tw:ml-1">Add a server</span>
|
<FontAwesomeIcon icon={plusIcon} /> Add a server
|
||||||
</DropdownItem>
|
</Dropdown.Item>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{serversList.map(({ name, id }) => (
|
{serversList.map(({ name, id }) => (
|
||||||
<DropdownItem key={id} tag={Link} to={`/server/${id}`} active={getServerId(selectedServer) === id}>
|
<Dropdown.Item key={id} to={`/server/${id}`} selected={getServerId(selectedServer) === id}>
|
||||||
{name}
|
{name}
|
||||||
</DropdownItem>
|
</Dropdown.Item>
|
||||||
))}
|
))}
|
||||||
<DropdownItem divider tag="hr" />
|
<Dropdown.Separator />
|
||||||
<DropdownItem tag={Link} to="/manage-servers">
|
<Dropdown.Item to="/manage-servers">
|
||||||
<FontAwesomeIcon icon={serverIcon} /> <span className="tw:ml-1">Manage servers</span>
|
<FontAwesomeIcon icon={serverIcon} /> Manage servers
|
||||||
</DropdownItem>
|
</Dropdown.Item>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</DropdownMenu>
|
</NavBar.Dropdown>
|
||||||
</UncontrolledDropdown>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -8,5 +8,7 @@
|
|||||||
:root {
|
:root {
|
||||||
--footer-height: 2.3rem;
|
--footer-height: 2.3rem;
|
||||||
--footer-margin: .8rem;
|
--footer-margin: .8rem;
|
||||||
|
/* Temp alias fo header-height to tw-header-height, so that shlink-web-component uses the right value */
|
||||||
|
--header-height: var(--tw-header-height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { screen, waitFor } from '@testing-library/react';
|
import { screen } from '@testing-library/react';
|
||||||
import { fromPartial } from '@total-typescript/shoehorn';
|
import { fromPartial } from '@total-typescript/shoehorn';
|
||||||
import { createMemoryHistory } from 'history';
|
import { createMemoryHistory } from 'history';
|
||||||
import { Router } from 'react-router';
|
import { Router } from 'react-router';
|
||||||
@ -8,8 +8,8 @@ import { renderWithEvents } from '../__helpers__/setUpTest';
|
|||||||
|
|
||||||
describe('<MainHeader />', () => {
|
describe('<MainHeader />', () => {
|
||||||
const MainHeader = MainHeaderFactory(fromPartial({
|
const MainHeader = MainHeaderFactory(fromPartial({
|
||||||
// Fake this component as a li, as it gets rendered inside a ul
|
// Fake this component as a li[role="menuitem"], as it gets rendered inside a ul[role="menu"]
|
||||||
ServersDropdown: () => <li>ServersDropdown</li>,
|
ServersDropdown: () => <li role="menuitem">ServersDropdown</li>,
|
||||||
}));
|
}));
|
||||||
const setUp = (pathname = '') => {
|
const setUp = (pathname = '') => {
|
||||||
const history = createMemoryHistory();
|
const history = createMemoryHistory();
|
||||||
@ -37,35 +37,8 @@ describe('<MainHeader />', () => {
|
|||||||
['/settings/bar', true],
|
['/settings/bar', true],
|
||||||
])('sets link to settings as active only when current path is settings', (currentPath, isActive) => {
|
])('sets link to settings as active only when current path is settings', (currentPath, isActive) => {
|
||||||
setUp(currentPath);
|
setUp(currentPath);
|
||||||
|
expect(screen.getByRole('menuitem', { name: /Settings$/ })).toHaveAttribute(
|
||||||
if (isActive) {
|
'data-active', isActive ? 'true' : 'false',
|
||||||
expect(screen.getByText(/Settings$/).getAttribute('class')).toContain('active');
|
);
|
||||||
} else {
|
|
||||||
expect(screen.getByText(/Settings$/).getAttribute('class')).not.toContain('active');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('renders expected class based on the nav bar state', async () => {
|
|
||||||
const { user } = setUp();
|
|
||||||
|
|
||||||
const toggle = screen.getByLabelText('Toggle navigation');
|
|
||||||
const icon = toggle.firstChild;
|
|
||||||
|
|
||||||
expect(icon).not.toHaveClass('tw:rotate-180');
|
|
||||||
await user.click(toggle);
|
|
||||||
|
|
||||||
expect(icon).toHaveClass('tw:rotate-180');
|
|
||||||
await user.click(toggle);
|
|
||||||
expect(icon).not.toHaveClass('tw:rotate-180');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('opens Collapse when clicking toggle', async () => {
|
|
||||||
const { container, user } = setUp();
|
|
||||||
const collapse = container.querySelector('.collapse');
|
|
||||||
const toggle = screen.getByLabelText('Toggle navigation');
|
|
||||||
|
|
||||||
expect(collapse).not.toHaveAttribute('class', expect.stringContaining('show'));
|
|
||||||
await user.click(toggle);
|
|
||||||
await waitFor(() => expect(collapse).toHaveAttribute('class', expect.stringContaining('show')));
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -14,7 +14,7 @@ describe('<ServersDropdown />', () => {
|
|||||||
};
|
};
|
||||||
const setUp = (servers: ServersMap = fallbackServers) => renderWithEvents(
|
const setUp = (servers: ServersMap = fallbackServers) => renderWithEvents(
|
||||||
<MemoryRouter>
|
<MemoryRouter>
|
||||||
<ul>
|
<ul role="menu">
|
||||||
<ServersDropdown servers={servers} selectedServer={null} />
|
<ServersDropdown servers={servers} selectedServer={null} />
|
||||||
</ul>
|
</ul>
|
||||||
</MemoryRouter>,
|
</MemoryRouter>,
|
||||||
@ -33,16 +33,18 @@ describe('<ServersDropdown />', () => {
|
|||||||
|
|
||||||
await user.click(screen.getByText('Servers'));
|
await user.click(screen.getByText('Servers'));
|
||||||
const items = screen.getAllByRole('menuitem');
|
const items = screen.getAllByRole('menuitem');
|
||||||
expect(items).toHaveLength(Object.values(fallbackServers).length + 1);
|
|
||||||
expect(items[0]).toHaveTextContent('foo');
|
// We have to add two for the "Manage servers" and the "Settings" menu items
|
||||||
expect(items[1]).toHaveTextContent('bar');
|
expect(items).toHaveLength(Object.values(fallbackServers).length + 2);
|
||||||
expect(items[2]).toHaveTextContent('baz');
|
expect(items[1]).toHaveTextContent('foo');
|
||||||
expect(items[3]).toHaveTextContent('Manage servers');
|
expect(items[2]).toHaveTextContent('bar');
|
||||||
|
expect(items[3]).toHaveTextContent('baz');
|
||||||
|
expect(items[4]).toHaveTextContent('Manage servers');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('contains a toggle with proper text', () => {
|
it('contains a toggle with proper text', () => {
|
||||||
setUp();
|
setUp();
|
||||||
expect(screen.getByRole('link')).toHaveTextContent('Servers');
|
expect(screen.getByRole('button')).toHaveTextContent('Servers');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('contains a button to manage servers', async () => {
|
it('contains a button to manage servers', async () => {
|
||||||
@ -56,6 +58,6 @@ describe('<ServersDropdown />', () => {
|
|||||||
const { user } = setUp({});
|
const { user } = setUp({});
|
||||||
|
|
||||||
await user.click(screen.getByText('Servers'));
|
await user.click(screen.getByText('Servers'));
|
||||||
expect(screen.getByRole('menuitem')).toHaveTextContent('Add a server');
|
expect(screen.getByRole('menuitem', { name: 'Add a server' })).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user