mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2025-01-09 01:37:24 +03:00
Improved NavPills component and added test
This commit is contained in:
parent
b0fa14fcfe
commit
f24c8052a9
4 changed files with 70 additions and 27 deletions
|
@ -1,7 +1,7 @@
|
|||
import { FC, ReactNode } from 'react';
|
||||
import { Navigate, Routes, Route } from 'react-router-dom';
|
||||
import { NoMenuLayout } from '../common/NoMenuLayout';
|
||||
import { NavPills } from '../utils/NavPills';
|
||||
import { NavPillItem, NavPills } from '../utils/NavPills';
|
||||
|
||||
const SettingsSections: FC<{ items: ReactNode[] }> = ({ items }) => (
|
||||
<>
|
||||
|
@ -18,13 +18,11 @@ const Settings = (
|
|||
Tags: FC,
|
||||
) => () => (
|
||||
<NoMenuLayout>
|
||||
<NavPills
|
||||
items={[
|
||||
{ to: 'app', children: 'App' },
|
||||
{ to: 'short-urls', children: 'Short URLs' },
|
||||
{ to: 'others', children: 'Others' },
|
||||
]}
|
||||
/>
|
||||
<NavPills>
|
||||
<NavPillItem to="app">App</NavPillItem>
|
||||
<NavPillItem to="short-urls">Short URLs</NavPillItem>
|
||||
<NavPillItem to="others">Others</NavPillItem>
|
||||
</NavPills>
|
||||
|
||||
<Routes>
|
||||
<Route path="app" element={<SettingsSections items={[ <UserInterface key="one" />, <RealTimeUpdates key="two" /> ]} />} />
|
||||
|
|
|
@ -1,17 +1,29 @@
|
|||
import { FC, ReactNode } from 'react';
|
||||
import { FC, Children, isValidElement } from 'react';
|
||||
import { Card, Nav, NavLink } from 'reactstrap';
|
||||
import { NavLink as RouterNavLink } from 'react-router-dom';
|
||||
import './NavPills.scss';
|
||||
|
||||
interface NavPillsProps {
|
||||
items: { children: ReactNode; to: string; replace?: boolean }[];
|
||||
interface NavPillProps {
|
||||
to: string;
|
||||
replace?: boolean;
|
||||
}
|
||||
|
||||
export const NavPills: FC<NavPillsProps> = ({ items }) => (
|
||||
export const NavPillItem: FC<NavPillProps> = ({ children, ...rest }) => (
|
||||
<NavLink className="nav-pills__nav-link" tag={RouterNavLink} {...rest}>
|
||||
{children}
|
||||
</NavLink>
|
||||
);
|
||||
|
||||
export const NavPills: FC = ({ children }) => (
|
||||
<Card className="nav-pills__nav p-0 overflow-hidden mb-3" body>
|
||||
<Nav pills fill>
|
||||
{items.map(({ children, ...rest }, index) =>
|
||||
<NavLink key={index} className="nav-pills__nav-link" tag={RouterNavLink} {...rest}>{children}</NavLink>)}
|
||||
{Children.map(children, (child) => {
|
||||
if (!isValidElement(child) || child.type !== NavPillItem) {
|
||||
throw new Error('Only NavPillItem children are allowed inside NavPills.');
|
||||
}
|
||||
|
||||
return child;
|
||||
})}
|
||||
</Nav>
|
||||
</Card>
|
||||
);
|
||||
|
|
|
@ -15,7 +15,7 @@ import { Settings } from '../settings/reducers/settings';
|
|||
import { SelectedServer } from '../servers/data';
|
||||
import { supportsBotVisits } from '../utils/helpers/features';
|
||||
import { prettify } from '../utils/helpers/numbers';
|
||||
import { NavPills } from '../utils/NavPills';
|
||||
import { NavPillItem, NavPills } from '../utils/NavPills';
|
||||
import LineChartCard from './charts/LineChartCard';
|
||||
import VisitsTable from './VisitsTable';
|
||||
import { NormalizedOrphanVisit, NormalizedVisit, VisitsFilter, VisitsInfo, VisitsParams } from './types';
|
||||
|
@ -143,18 +143,14 @@ const VisitsStats: FC<VisitsStatsProps> = ({
|
|||
|
||||
return (
|
||||
<>
|
||||
<NavPills
|
||||
items={Object.values(sections).map(({ title, icon, subPath }) => ({
|
||||
replace: true,
|
||||
to: buildSectionUrl(subPath),
|
||||
children: (
|
||||
<>
|
||||
<FontAwesomeIcon icon={icon} />
|
||||
<span className="ml-2 d-none d-sm-inline">{title}</span>
|
||||
</>
|
||||
),
|
||||
}))}
|
||||
/>
|
||||
<NavPills>
|
||||
{Object.values(sections).map(({ title, icon, subPath }, index) => (
|
||||
<NavPillItem key={index} to={buildSectionUrl(subPath)} replace>
|
||||
<FontAwesomeIcon icon={icon} />
|
||||
<span className="ml-2 d-none d-sm-inline">{title}</span>
|
||||
</NavPillItem>
|
||||
))}
|
||||
</NavPills>
|
||||
<Row>
|
||||
<Routes>
|
||||
<Route
|
||||
|
|
37
test/utils/NavPills.test.tsx
Normal file
37
test/utils/NavPills.test.tsx
Normal file
|
@ -0,0 +1,37 @@
|
|||
import { shallow } from 'enzyme';
|
||||
import { Card, Nav } from 'reactstrap';
|
||||
import { NavPillItem, NavPills } from '../../src/utils/NavPills';
|
||||
|
||||
describe('<NavPills />', () => {
|
||||
it.each([
|
||||
[ 'Foo' ],
|
||||
[ <span key="1">Hi!</span> ],
|
||||
[[ <NavPillItem key="1" to="" />, <span key="2">Hi!</span> ]],
|
||||
])('throws error when any of the children is not a NavPillItem', (children) => {
|
||||
expect.assertions(1);
|
||||
|
||||
try {
|
||||
shallow(<NavPills>{children}</NavPills>);
|
||||
} catch (e: any) {
|
||||
expect(e.message).toEqual('Only NavPillItem children are allowed inside NavPills.');
|
||||
}
|
||||
});
|
||||
|
||||
it('renders provided items', () => {
|
||||
const wrapper = shallow(
|
||||
<NavPills>
|
||||
<NavPillItem to="1">1</NavPillItem>
|
||||
<NavPillItem to="2">2</NavPillItem>
|
||||
<NavPillItem to="3">3</NavPillItem>
|
||||
</NavPills>,
|
||||
);
|
||||
const card = wrapper.find(Card);
|
||||
const nav = wrapper.find(Nav);
|
||||
|
||||
expect(card).toHaveLength(1);
|
||||
expect(card.prop('body')).toEqual(true);
|
||||
expect(nav).toHaveLength(1);
|
||||
expect(nav.prop('pills')).toEqual(true);
|
||||
expect(nav.prop('fill')).toEqual(true);
|
||||
});
|
||||
});
|
Loading…
Reference in a new issue