mirror of
https://github.com/shlinkio/shlink-web-client.git
synced 2025-01-09 01:37:24 +03:00
Remove sidebar reducer, which couple web-client with web-component
This commit is contained in:
parent
c3b6ce34ba
commit
5a9640bd57
10 changed files with 38 additions and 87 deletions
|
@ -3,8 +3,8 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
import { useToggle } from '@shlinkio/shlink-frontend-kit';
|
import { useToggle } from '@shlinkio/shlink-frontend-kit';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import type { FC, ReactNode } from 'react';
|
import type { FC, ReactNode } from 'react';
|
||||||
import { useEffect } from 'react';
|
import { Fragment, useEffect, useMemo } from 'react';
|
||||||
import { Navigate, Route, Routes, useLocation } from 'react-router-dom';
|
import { BrowserRouter, Navigate, Route, Routes, useInRouterContext, useLocation } from 'react-router-dom';
|
||||||
import { AsideMenu } from './common/AsideMenu';
|
import { AsideMenu } from './common/AsideMenu';
|
||||||
import { useFeature } from './utils/features';
|
import { useFeature } from './utils/features';
|
||||||
import { useSwipeable } from './utils/helpers/hooks';
|
import { useSwipeable } from './utils/helpers/hooks';
|
||||||
|
@ -30,6 +30,9 @@ export const Main = (
|
||||||
): FC<MainProps> => ({ createNotFound }) => {
|
): FC<MainProps> => ({ createNotFound }) => {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const routesPrefix = useRoutesPrefix();
|
const routesPrefix = useRoutesPrefix();
|
||||||
|
const inRouterContext = useInRouterContext();
|
||||||
|
const Wrapper = useMemo(() => (inRouterContext ? Fragment : BrowserRouter), [inRouterContext]);
|
||||||
|
|
||||||
const [sidebarVisible, toggleSidebar, showSidebar, hideSidebar] = useToggle();
|
const [sidebarVisible, toggleSidebar, showSidebar, hideSidebar] = useToggle();
|
||||||
useEffect(() => hideSidebar(), [location]);
|
useEffect(() => hideSidebar(), [location]);
|
||||||
|
|
||||||
|
@ -37,10 +40,10 @@ export const Main = (
|
||||||
const burgerClasses = classNames('menu-layout__burger-icon', { 'menu-layout__burger-icon--active': sidebarVisible });
|
const burgerClasses = classNames('menu-layout__burger-icon', { 'menu-layout__burger-icon--active': sidebarVisible });
|
||||||
const swipeableProps = useSwipeable(showSidebar, hideSidebar);
|
const swipeableProps = useSwipeable(showSidebar, hideSidebar);
|
||||||
|
|
||||||
// FIXME Check if this is already wrapped by a router, and wrap otherwise
|
// FIXME Check if this works when not currently wrapped in a router
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Wrapper basename={routesPrefix}>
|
||||||
<FontAwesomeIcon icon={burgerIcon} className={burgerClasses} onClick={toggleSidebar} />
|
<FontAwesomeIcon icon={burgerIcon} className={burgerClasses} onClick={toggleSidebar} />
|
||||||
|
|
||||||
<div {...swipeableProps} className="menu-layout__swipeable">
|
<div {...swipeableProps} className="menu-layout__swipeable">
|
||||||
|
@ -67,6 +70,6 @@ export const Main = (
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</Wrapper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,17 +1,22 @@
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
import { useLocation } from 'react-router-dom';
|
||||||
import type { SelectedServer } from '../servers/data';
|
import type { SelectedServer } from '../servers/data';
|
||||||
import type { Sidebar } from './reducers/sidebar';
|
|
||||||
import { ShlinkVersions } from './ShlinkVersions';
|
import { ShlinkVersions } from './ShlinkVersions';
|
||||||
import './ShlinkVersionsContainer.scss';
|
import './ShlinkVersionsContainer.scss';
|
||||||
|
|
||||||
export interface ShlinkVersionsContainerProps {
|
export type ShlinkVersionsContainerProps = {
|
||||||
selectedServer: SelectedServer;
|
selectedServer: SelectedServer;
|
||||||
sidebar: Sidebar;
|
};
|
||||||
}
|
|
||||||
|
const SHLINK_CONTAINER_PATH_PATTERN = /^\/server\/[a-zA-Z0-9-]*\/(?!edit)/;
|
||||||
|
|
||||||
|
export const ShlinkVersionsContainer = ({ selectedServer }: ShlinkVersionsContainerProps) => {
|
||||||
|
const { pathname } = useLocation();
|
||||||
|
const withPadding = useMemo(() => SHLINK_CONTAINER_PATH_PATTERN.test(pathname), [pathname]);
|
||||||
|
|
||||||
export const ShlinkVersionsContainer = ({ selectedServer, sidebar }: ShlinkVersionsContainerProps) => {
|
|
||||||
const classes = classNames('text-center', {
|
const classes = classNames('text-center', {
|
||||||
'shlink-versions-container--with-sidebar': sidebar.sidebarPresent,
|
'shlink-versions-container--with-sidebar': withPadding,
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import type { Settings, ShlinkWebComponentType, TagColorsStorage } from '@shlinkio/shlink-web-component';
|
import type { Settings, ShlinkWebComponentType, TagColorsStorage } from '@shlinkio/shlink-web-component';
|
||||||
import type { FC } from 'react';
|
import type { FC } from 'react';
|
||||||
import { useEffect } from 'react';
|
|
||||||
import type { ShlinkApiClientBuilder } from '../api/services/ShlinkApiClientBuilder';
|
import type { ShlinkApiClientBuilder } from '../api/services/ShlinkApiClientBuilder';
|
||||||
import { isReachableServer } from '../servers/data';
|
import { isReachableServer } from '../servers/data';
|
||||||
import { withSelectedServer } from '../servers/helpers/withSelectedServer';
|
import { withSelectedServer } from '../servers/helpers/withSelectedServer';
|
||||||
|
@ -8,8 +7,6 @@ import { NotFound } from './NotFound';
|
||||||
import './ShlinkWebComponentContainer.scss';
|
import './ShlinkWebComponentContainer.scss';
|
||||||
|
|
||||||
interface ShlinkWebComponentContainerProps {
|
interface ShlinkWebComponentContainerProps {
|
||||||
sidebarPresent: Function;
|
|
||||||
sidebarNotPresent: Function;
|
|
||||||
settings: Settings;
|
settings: Settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,17 +15,10 @@ export const ShlinkWebComponentContainer = (
|
||||||
tagColorsStorage: TagColorsStorage,
|
tagColorsStorage: TagColorsStorage,
|
||||||
ShlinkWebComponent: ShlinkWebComponentType,
|
ShlinkWebComponent: ShlinkWebComponentType,
|
||||||
ServerError: FC,
|
ServerError: FC,
|
||||||
) => withSelectedServer<ShlinkWebComponentContainerProps>((
|
) => withSelectedServer<ShlinkWebComponentContainerProps>(({ selectedServer, settings }) => {
|
||||||
{ selectedServer, sidebarNotPresent, sidebarPresent, settings },
|
|
||||||
) => {
|
|
||||||
const selectedServerIsReachable = isReachableServer(selectedServer);
|
const selectedServerIsReachable = isReachableServer(selectedServer);
|
||||||
const routesPrefix = selectedServerIsReachable ? `/server/${selectedServer.id}` : '';
|
const routesPrefix = selectedServerIsReachable ? `/server/${selectedServer.id}` : '';
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
selectedServerIsReachable && sidebarPresent();
|
|
||||||
return () => sidebarNotPresent();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
if (!selectedServerIsReachable) {
|
if (!selectedServerIsReachable) {
|
||||||
return <ServerError />;
|
return <ServerError />;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
import { createSlice } from '@reduxjs/toolkit';
|
|
||||||
|
|
||||||
// FIXME This is only used for some components to have extra paddings/styles if existing section has a side menu
|
|
||||||
// Now that's basically the route which renders ShlinkWebComponent, so maybe there's some way to re-think this
|
|
||||||
// logic, and perhaps get rid of a reducer just for that
|
|
||||||
|
|
||||||
export interface Sidebar {
|
|
||||||
sidebarPresent: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const initialState: Sidebar = {
|
|
||||||
sidebarPresent: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
const { actions, reducer } = createSlice({
|
|
||||||
name: 'shlink/sidebar',
|
|
||||||
initialState,
|
|
||||||
reducers: {
|
|
||||||
sidebarPresent: () => ({ sidebarPresent: true }),
|
|
||||||
sidebarNotPresent: () => ({ sidebarPresent: false }),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export const { sidebarPresent, sidebarNotPresent } = actions;
|
|
||||||
|
|
||||||
export const sidebarReducer = reducer;
|
|
|
@ -5,7 +5,6 @@ import { withoutSelectedServer } from '../../servers/helpers/withoutSelectedServ
|
||||||
import { ErrorHandler } from '../ErrorHandler';
|
import { ErrorHandler } from '../ErrorHandler';
|
||||||
import { Home } from '../Home';
|
import { Home } from '../Home';
|
||||||
import { MainHeader } from '../MainHeader';
|
import { MainHeader } from '../MainHeader';
|
||||||
import { sidebarNotPresent, sidebarPresent } from '../reducers/sidebar';
|
|
||||||
import { ScrollToTop } from '../ScrollToTop';
|
import { ScrollToTop } from '../ScrollToTop';
|
||||||
import { ShlinkVersionsContainer } from '../ShlinkVersionsContainer';
|
import { ShlinkVersionsContainer } from '../ShlinkVersionsContainer';
|
||||||
import { ShlinkWebComponentContainer } from '../ShlinkWebComponentContainer';
|
import { ShlinkWebComponentContainer } from '../ShlinkWebComponentContainer';
|
||||||
|
@ -36,17 +35,10 @@ export const provideServices = (bottle: Bottle, connect: ConnectDecorator) => {
|
||||||
'ShlinkWebComponent',
|
'ShlinkWebComponent',
|
||||||
'ServerError',
|
'ServerError',
|
||||||
);
|
);
|
||||||
bottle.decorator('ShlinkWebComponentContainer', connect(
|
bottle.decorator('ShlinkWebComponentContainer', connect(['selectedServer', 'settings'], ['selectServer']));
|
||||||
['selectedServer', 'settings'],
|
|
||||||
['selectServer', 'sidebarPresent', 'sidebarNotPresent'],
|
|
||||||
));
|
|
||||||
|
|
||||||
bottle.serviceFactory('ShlinkVersionsContainer', () => ShlinkVersionsContainer);
|
bottle.serviceFactory('ShlinkVersionsContainer', () => ShlinkVersionsContainer);
|
||||||
bottle.decorator('ShlinkVersionsContainer', connect(['selectedServer', 'sidebar']));
|
bottle.decorator('ShlinkVersionsContainer', connect(['selectedServer']));
|
||||||
|
|
||||||
bottle.serviceFactory('ErrorHandler', ErrorHandler, 'window', 'console');
|
bottle.serviceFactory('ErrorHandler', ErrorHandler, 'window', 'console');
|
||||||
|
|
||||||
// Actions
|
|
||||||
bottle.serviceFactory('sidebarPresent', () => sidebarPresent);
|
|
||||||
bottle.serviceFactory('sidebarNotPresent', () => sidebarNotPresent);
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import type { Settings } from '@shlinkio/shlink-web-component';
|
import type { Settings } from '@shlinkio/shlink-web-component';
|
||||||
import type { Sidebar } from '../common/reducers/sidebar';
|
|
||||||
import type { SelectedServer, ServersMap } from '../servers/data';
|
import type { SelectedServer, ServersMap } from '../servers/data';
|
||||||
|
|
||||||
export interface ShlinkState {
|
export interface ShlinkState {
|
||||||
|
@ -7,7 +6,6 @@ export interface ShlinkState {
|
||||||
selectedServer: SelectedServer;
|
selectedServer: SelectedServer;
|
||||||
settings: Settings;
|
settings: Settings;
|
||||||
appUpdated: boolean;
|
appUpdated: boolean;
|
||||||
sidebar: Sidebar;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ConnectDecorator = (props: string[] | null, actions?: string[]) => any;
|
export type ConnectDecorator = (props: string[] | null, actions?: string[]) => any;
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { combineReducers } from '@reduxjs/toolkit';
|
import { combineReducers } from '@reduxjs/toolkit';
|
||||||
import type { IContainer } from 'bottlejs';
|
import type { IContainer } from 'bottlejs';
|
||||||
import { appUpdatesReducer } from '../app/reducers/appUpdates';
|
import { appUpdatesReducer } from '../app/reducers/appUpdates';
|
||||||
import { sidebarReducer } from '../common/reducers/sidebar';
|
|
||||||
import type { ShlinkState } from '../container/types';
|
import type { ShlinkState } from '../container/types';
|
||||||
import { serversReducer } from '../servers/reducers/servers';
|
import { serversReducer } from '../servers/reducers/servers';
|
||||||
import { settingsReducer } from '../settings/reducers/settings';
|
import { settingsReducer } from '../settings/reducers/settings';
|
||||||
|
@ -11,5 +10,4 @@ export const initReducers = (container: IContainer) => combineReducers<ShlinkSta
|
||||||
servers: serversReducer,
|
servers: serversReducer,
|
||||||
selectedServer: container.selectedServerReducer,
|
selectedServer: container.selectedServerReducer,
|
||||||
settings: settingsReducer,
|
settings: settingsReducer,
|
||||||
sidebar: sidebarReducer,
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,16 +1,25 @@
|
||||||
import { render } from '@testing-library/react';
|
import { render } from '@testing-library/react';
|
||||||
import { fromPartial } from '@total-typescript/shoehorn';
|
import { fromPartial } from '@total-typescript/shoehorn';
|
||||||
import type { Sidebar } from '../../src/common/reducers/sidebar';
|
import { createMemoryHistory } from 'history';
|
||||||
|
import { Router } from 'react-router-dom';
|
||||||
import { ShlinkVersionsContainer } from '../../src/common/ShlinkVersionsContainer';
|
import { ShlinkVersionsContainer } from '../../src/common/ShlinkVersionsContainer';
|
||||||
|
|
||||||
describe('<ShlinkVersionsContainer />', () => {
|
describe('<ShlinkVersionsContainer />', () => {
|
||||||
const setUp = (sidebar: Sidebar) => render(
|
const setUp = (activeRoute: string) => {
|
||||||
<ShlinkVersionsContainer selectedServer={fromPartial({})} sidebar={sidebar} />,
|
const history = createMemoryHistory();
|
||||||
);
|
history.push(activeRoute);
|
||||||
|
|
||||||
|
return render(
|
||||||
|
<Router location={history.location} navigator={history}>
|
||||||
|
<ShlinkVersionsContainer selectedServer={fromPartial({})} />
|
||||||
|
</Router>,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
it.each([
|
it.each([
|
||||||
[{ sidebarPresent: false }, 'text-center'],
|
['/something', 'text-center'],
|
||||||
[{ sidebarPresent: true }, 'text-center shlink-versions-container--with-sidebar'],
|
['/server/foo/edit', 'text-center'],
|
||||||
|
['/server/foo/bar', 'text-center shlink-versions-container--with-sidebar'],
|
||||||
])('renders proper col classes based on sidebar status', (sidebar, expectedClasses) => {
|
])('renders proper col classes based on sidebar status', (sidebar, expectedClasses) => {
|
||||||
const { container } = setUp(sidebar);
|
const { container } = setUp(sidebar);
|
||||||
expect(container.firstChild).toHaveAttribute('class', `${expectedClasses}`);
|
expect(container.firstChild).toHaveAttribute('class', `${expectedClasses}`);
|
||||||
|
|
|
@ -17,13 +17,7 @@ describe('<ShlinkWebComponentContainer />', () => {
|
||||||
() => <>ServerError</>,
|
() => <>ServerError</>,
|
||||||
);
|
);
|
||||||
const setUp = (selectedServer: SelectedServer) => render(
|
const setUp = (selectedServer: SelectedServer) => render(
|
||||||
<ShlinkWebComponentContainer
|
<ShlinkWebComponentContainer selectServer={vi.fn()} selectedServer={selectedServer} settings={{}} />,
|
||||||
sidebarNotPresent={vi.fn()}
|
|
||||||
sidebarPresent={vi.fn()}
|
|
||||||
selectServer={vi.fn()}
|
|
||||||
selectedServer={selectedServer}
|
|
||||||
settings={{}}
|
|
||||||
/>,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
import { sidebarNotPresent, sidebarPresent, sidebarReducer } from '../../../src/common/reducers/sidebar';
|
|
||||||
|
|
||||||
describe('sidebarReducer', () => {
|
|
||||||
describe('reducer', () => {
|
|
||||||
it.each([
|
|
||||||
[sidebarPresent, { sidebarPresent: true }],
|
|
||||||
[sidebarNotPresent, { sidebarPresent: false }],
|
|
||||||
])('returns expected on %s', (actionCreator, expected) => {
|
|
||||||
expect(sidebarReducer(undefined, actionCreator())).toEqual(expected);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
Loading…
Reference in a new issue