Refactor Layout (#322)
* Refactor Layout * Fix storybook * Fixing tests by forcing msw version before regression
This commit is contained in:
@ -127,7 +127,7 @@
|
|||||||
"eslint-plugin-twenty": "file:../packages/eslint-plugin-twenty",
|
"eslint-plugin-twenty": "file:../packages/eslint-plugin-twenty",
|
||||||
"http-server": "^14.1.1",
|
"http-server": "^14.1.1",
|
||||||
"mock-apollo-client": "^1.2.1",
|
"mock-apollo-client": "^1.2.1",
|
||||||
"msw": "^1.2.1",
|
"msw": "1.2.1",
|
||||||
"msw-storybook-addon": "^1.8.0",
|
"msw-storybook-addon": "^1.8.0",
|
||||||
"prettier": "^2.8.0",
|
"prettier": "^2.8.0",
|
||||||
"prop-types": "^15.8.1",
|
"prop-types": "^15.8.1",
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
/* tslint:disable */
|
/* tslint:disable */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mock Service Worker (1.2.2).
|
* Mock Service Worker (1.2.1).
|
||||||
* @see https://github.com/mswjs/msw
|
* @see https://github.com/mswjs/msw
|
||||||
* - Please do NOT modify this file.
|
* - Please do NOT modify this file.
|
||||||
* - Please do NOT serve this file on production.
|
* - Please do NOT serve this file on production.
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
import { Navigate, Route, Routes } from 'react-router-dom';
|
import { Navigate, Route, Routes } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { DefaultLayout } from '@/ui/layout/DefaultLayout';
|
||||||
|
|
||||||
import { RequireAuth } from './modules/auth/components/RequireAuth';
|
import { RequireAuth } from './modules/auth/components/RequireAuth';
|
||||||
import { AuthCallback } from './pages/auth/AuthCallback';
|
import { AuthCallback } from './pages/auth/AuthCallback';
|
||||||
import { Login } from './pages/auth/Login';
|
import { Login } from './pages/auth/Login';
|
||||||
@ -10,37 +12,39 @@ import { SettingsProfile } from './pages/settings/SettingsProfile';
|
|||||||
|
|
||||||
export function App() {
|
export function App() {
|
||||||
return (
|
return (
|
||||||
<Routes>
|
<DefaultLayout>
|
||||||
<Route
|
<Routes>
|
||||||
path="*"
|
<Route
|
||||||
element={
|
path="*"
|
||||||
<RequireAuth>
|
element={
|
||||||
|
<RequireAuth>
|
||||||
|
<Routes>
|
||||||
|
<Route path="" element={<Navigate to="/people" replace />} />
|
||||||
|
<Route path="people" element={<People />} />
|
||||||
|
<Route path="companies" element={<Companies />} />
|
||||||
|
<Route path="opportunities" element={<Opportunities />} />
|
||||||
|
</Routes>
|
||||||
|
</RequireAuth>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="auth/*"
|
||||||
|
element={
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="" element={<Navigate to="/people" replace />} />
|
<Route path="callback" element={<AuthCallback />} />
|
||||||
<Route path="people" element={<People />} />
|
<Route path="login" element={<Login />} />
|
||||||
<Route path="companies" element={<Companies />} />
|
|
||||||
<Route path="opportunities" element={<Opportunities />} />
|
|
||||||
</Routes>
|
</Routes>
|
||||||
</RequireAuth>
|
}
|
||||||
}
|
/>
|
||||||
/>
|
<Route
|
||||||
<Route
|
path="settings/*"
|
||||||
path="auth/*"
|
element={
|
||||||
element={
|
<Routes>
|
||||||
<Routes>
|
<Route path="profile" element={<SettingsProfile />} />
|
||||||
<Route path="callback" element={<AuthCallback />} />
|
</Routes>
|
||||||
<Route path="login" element={<Login />} />
|
}
|
||||||
</Routes>
|
/>
|
||||||
}
|
</Routes>
|
||||||
/>
|
</DefaultLayout>
|
||||||
<Route
|
|
||||||
path="settings/*"
|
|
||||||
element={
|
|
||||||
<Routes>
|
|
||||||
<Route path="profile" element={<SettingsProfile />} />
|
|
||||||
</Routes>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Routes>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { useMatch, useResolvedPath } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
import { useTheme } from '@emotion/react';
|
import { useTheme } from '@emotion/react';
|
||||||
|
|
||||||
|
import { SettingsNavbar } from '@/settings/components/SettingsNavbar';
|
||||||
import {
|
import {
|
||||||
IconBuildingSkyscraper,
|
IconBuildingSkyscraper,
|
||||||
IconInbox,
|
IconInbox,
|
||||||
@ -16,51 +17,51 @@ import NavWorkspaceButton from './modules/ui/layout/navbar/NavWorkspaceButton';
|
|||||||
|
|
||||||
export function AppNavbar() {
|
export function AppNavbar() {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
const currentPath = useLocation().pathname;
|
||||||
|
|
||||||
|
const shouldDiplaySubNavBar = currentPath.match(/\/settings\//g) !== null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<NavWorkspaceButton />
|
{!shouldDiplaySubNavBar ? (
|
||||||
<NavItemsContainer>
|
<>
|
||||||
<NavItem
|
<NavWorkspaceButton />
|
||||||
label="Search"
|
<NavItemsContainer>
|
||||||
to="/search"
|
<NavItem
|
||||||
icon={<IconSearch size={theme.iconSizeMedium} />}
|
label="Search"
|
||||||
soon={true}
|
to="/search"
|
||||||
/>
|
icon={<IconSearch size={theme.iconSizeMedium} />}
|
||||||
<NavItem
|
soon={true}
|
||||||
label="Inbox"
|
/>
|
||||||
to="/inbox"
|
<NavItem
|
||||||
icon={<IconInbox size={theme.iconSizeMedium} />}
|
label="Inbox"
|
||||||
soon={true}
|
to="/inbox"
|
||||||
/>
|
icon={<IconInbox size={theme.iconSizeMedium} />}
|
||||||
<NavItem
|
soon={true}
|
||||||
label="Settings"
|
/>
|
||||||
to="/settings/profile"
|
<NavItem
|
||||||
icon={<IconSettings size={theme.iconSizeMedium} />}
|
label="Settings"
|
||||||
/>
|
to="/settings/profile"
|
||||||
<NavTitle label="Workspace" />
|
icon={<IconSettings size={theme.iconSizeMedium} />}
|
||||||
<NavItem
|
/>
|
||||||
label="People"
|
<NavTitle label="Workspace" />
|
||||||
to="/people"
|
<NavItem
|
||||||
icon={<IconUser size={theme.iconSizeMedium} />}
|
label="People"
|
||||||
active={
|
to="/people"
|
||||||
!!useMatch({
|
icon={<IconUser size={theme.iconSizeMedium} />}
|
||||||
path: useResolvedPath('/people').pathname,
|
active={currentPath === '/people'}
|
||||||
end: true,
|
/>
|
||||||
})
|
<NavItem
|
||||||
}
|
label="Companies"
|
||||||
/>
|
to="/companies"
|
||||||
<NavItem
|
icon={<IconBuildingSkyscraper size={theme.iconSizeMedium} />}
|
||||||
label="Companies"
|
active={currentPath === '/companies'}
|
||||||
to="/companies"
|
/>
|
||||||
icon={<IconBuildingSkyscraper size={theme.iconSizeMedium} />}
|
</NavItemsContainer>
|
||||||
active={
|
</>
|
||||||
!!useMatch({
|
) : (
|
||||||
path: useResolvedPath('/companies').pathname,
|
<SettingsNavbar />
|
||||||
end: true,
|
)}
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</NavItemsContainer>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +0,0 @@
|
|||||||
import { DefaultLayout } from '@/ui/layout/DefaultLayout';
|
|
||||||
|
|
||||||
import { AppNavbar } from './AppNavbar';
|
|
||||||
|
|
||||||
type OwnProps = {
|
|
||||||
children: JSX.Element;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function AppPage({ children }: OwnProps) {
|
|
||||||
return <DefaultLayout Navbar={AppNavbar}>{children}</DefaultLayout>;
|
|
||||||
}
|
|
||||||
@ -1,6 +1,7 @@
|
|||||||
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
import type { Meta, StoryObj } from '@storybook/react';
|
import type { Meta, StoryObj } from '@storybook/react';
|
||||||
|
|
||||||
import { getRenderWrapperForPage } from '~/testing/renderWrappers';
|
import { getRenderWrapperForComponent } from '~/testing/renderWrappers';
|
||||||
|
|
||||||
import { CommandMenu } from '../CommandMenu';
|
import { CommandMenu } from '../CommandMenu';
|
||||||
|
|
||||||
@ -13,5 +14,9 @@ export default meta;
|
|||||||
type Story = StoryObj<typeof CommandMenu>;
|
type Story = StoryObj<typeof CommandMenu>;
|
||||||
|
|
||||||
export const Default: Story = {
|
export const Default: Story = {
|
||||||
render: getRenderWrapperForPage(<CommandMenu initiallyOpen={true} />),
|
render: getRenderWrapperForComponent(
|
||||||
|
<MemoryRouter>
|
||||||
|
<CommandMenu initiallyOpen={true} />
|
||||||
|
</MemoryRouter>,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|||||||
@ -8,16 +8,15 @@ import {
|
|||||||
IconSettings,
|
IconSettings,
|
||||||
IconUser,
|
IconUser,
|
||||||
} from '@/ui/icons/index';
|
} from '@/ui/icons/index';
|
||||||
import NavBackButton from '@/ui/layout/navbar//NavBackButton';
|
|
||||||
import NavItem from '@/ui/layout/navbar/NavItem';
|
import NavItem from '@/ui/layout/navbar/NavItem';
|
||||||
import NavItemsContainer from '@/ui/layout/navbar/NavItemsContainer';
|
import NavItemsContainer from '@/ui/layout/navbar/NavItemsContainer';
|
||||||
import NavTitle from '@/ui/layout/navbar/NavTitle';
|
import NavTitle from '@/ui/layout/navbar/NavTitle';
|
||||||
|
import SubNavbarContainer from '@/ui/layout/navbar/sub-navbar/SubNavBarContainer';
|
||||||
|
|
||||||
export function SettingsNavbar() {
|
export function SettingsNavbar() {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
return (
|
return (
|
||||||
<>
|
<SubNavbarContainer backButtonTitle="Settings">
|
||||||
<NavBackButton title="Settings" />
|
|
||||||
<NavItemsContainer>
|
<NavItemsContainer>
|
||||||
<NavTitle label="User" />
|
<NavTitle label="User" />
|
||||||
<NavItem
|
<NavItem
|
||||||
@ -64,6 +63,6 @@ export function SettingsNavbar() {
|
|||||||
danger={true}
|
danger={true}
|
||||||
/>
|
/>
|
||||||
</NavItemsContainer>
|
</NavItemsContainer>
|
||||||
</>
|
</SubNavbarContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +0,0 @@
|
|||||||
import { SecondaryLayout } from '@/ui/layout/SecondaryLayout';
|
|
||||||
|
|
||||||
import { SettingsNavbar } from './SettingsNavbar';
|
|
||||||
|
|
||||||
type OwnProps = {
|
|
||||||
children: JSX.Element;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function SettingsPage({ children }: OwnProps) {
|
|
||||||
return <SecondaryLayout Navbar={SettingsNavbar}>{children}</SecondaryLayout>;
|
|
||||||
}
|
|
||||||
@ -3,6 +3,7 @@ import { useRecoilState, useRecoilValue } from 'recoil';
|
|||||||
|
|
||||||
import { currentUserState } from '@/auth/states/currentUserState';
|
import { currentUserState } from '@/auth/states/currentUserState';
|
||||||
import { CommandMenu } from '@/search/components/CommandMenu';
|
import { CommandMenu } from '@/search/components/CommandMenu';
|
||||||
|
import { AppNavbar } from '~/AppNavbar';
|
||||||
|
|
||||||
import { NavbarContainer } from './navbar/NavbarContainer';
|
import { NavbarContainer } from './navbar/NavbarContainer';
|
||||||
import { isNavbarOpenedState } from './states/isNavbarOpenedState';
|
import { isNavbarOpenedState } from './states/isNavbarOpenedState';
|
||||||
@ -25,9 +26,8 @@ const MainContainer = styled.div`
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
width: ${() =>
|
width: ${() =>
|
||||||
useRecoilValue(isNavbarOpenedState)
|
useRecoilValue(isNavbarOpenedState)
|
||||||
? `(calc(100% - ${NAVBAR_WIDTH})`
|
? `calc(100% - ${NAVBAR_WIDTH})`
|
||||||
: '100%'};
|
: '100%'};
|
||||||
|
|
||||||
@media (max-width: ${MOBILE_VIEWPORT}px) {
|
@media (max-width: ${MOBILE_VIEWPORT}px) {
|
||||||
width: ${() => (useRecoilValue(isNavbarOpenedState) ? '0' : '100%')};
|
width: ${() => (useRecoilValue(isNavbarOpenedState) ? '0' : '100%')};
|
||||||
}
|
}
|
||||||
@ -35,10 +35,9 @@ const MainContainer = styled.div`
|
|||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
children: JSX.Element;
|
children: JSX.Element;
|
||||||
Navbar: () => JSX.Element;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export function DefaultLayout({ children, Navbar }: OwnProps) {
|
export function DefaultLayout({ children }: OwnProps) {
|
||||||
const currentUser = useRecoilState(currentUserState);
|
const currentUser = useRecoilState(currentUserState);
|
||||||
const userIsAuthenticated = !!currentUser;
|
const userIsAuthenticated = !!currentUser;
|
||||||
|
|
||||||
@ -48,7 +47,7 @@ export function DefaultLayout({ children, Navbar }: OwnProps) {
|
|||||||
<>
|
<>
|
||||||
<CommandMenu />
|
<CommandMenu />
|
||||||
<NavbarContainer>
|
<NavbarContainer>
|
||||||
<Navbar />
|
<AppNavbar />
|
||||||
</NavbarContainer>
|
</NavbarContainer>
|
||||||
<MainContainer>{children}</MainContainer>
|
<MainContainer>{children}</MainContainer>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@ -6,6 +6,7 @@ const StyledPanel = styled.div`
|
|||||||
border: 1px solid ${(props) => props.theme.primaryBorder};
|
border: 1px solid ${(props) => props.theme.primaryBorder};
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
@ -1,68 +0,0 @@
|
|||||||
import styled from '@emotion/styled';
|
|
||||||
|
|
||||||
import { NavbarContainer } from './navbar/NavbarContainer';
|
|
||||||
import { MOBILE_VIEWPORT } from './styles/themes';
|
|
||||||
|
|
||||||
type OwnProps = {
|
|
||||||
children: JSX.Element;
|
|
||||||
Navbar: () => JSX.Element;
|
|
||||||
};
|
|
||||||
|
|
||||||
const StyledLayout = styled.div`
|
|
||||||
background: ${(props) => props.theme.noisyBackground};
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
height: 100vh;
|
|
||||||
position: relative;
|
|
||||||
width: 100vw;
|
|
||||||
`;
|
|
||||||
const MainContainer = styled.div`
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
overflow: hidden;
|
|
||||||
width: 100%;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const SubContainer = styled.div`
|
|
||||||
background: ${(props) => props.theme.primaryBackground};
|
|
||||||
border: 1px solid ${(props) => props.theme.primaryBorder};
|
|
||||||
border-radius: ${(props) => props.theme.spacing(2)};
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
margin: ${(props) => props.theme.spacing(4)};
|
|
||||||
max-width: calc(100vw - 500px);
|
|
||||||
padding: ${(props) => props.theme.spacing(8)};
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
@media (max-width: ${MOBILE_VIEWPORT}px) {
|
|
||||||
width: 100%;
|
|
||||||
max-width: none;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const SubSubContainer = styled.div`
|
|
||||||
color: ${(props) => props.theme.text100};
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 32px;
|
|
||||||
width: 350px;
|
|
||||||
|
|
||||||
@media (max-width: ${MOBILE_VIEWPORT}px) {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const SecondaryLayout = ({ children, Navbar }: OwnProps) => {
|
|
||||||
return (
|
|
||||||
<StyledLayout>
|
|
||||||
<NavbarContainer layout="secondary">
|
|
||||||
<Navbar />
|
|
||||||
</NavbarContainer>
|
|
||||||
<MainContainer>
|
|
||||||
<SubContainer>
|
|
||||||
<SubSubContainer>{children}</SubSubContainer>
|
|
||||||
</SubContainer>
|
|
||||||
</MainContainer>
|
|
||||||
</StyledLayout>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
53
front/src/modules/ui/layout/containers/ContentContainer.tsx
Normal file
53
front/src/modules/ui/layout/containers/ContentContainer.tsx
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import styled from '@emotion/styled';
|
||||||
|
import { useRecoilState } from 'recoil';
|
||||||
|
|
||||||
|
import { Panel } from '../Panel';
|
||||||
|
import { RightDrawer } from '../right-drawer/components/RightDrawer';
|
||||||
|
import { isRightDrawerOpenState } from '../right-drawer/states/isRightDrawerOpenState';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
children: JSX.Element;
|
||||||
|
topMargin?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const MainContainer = styled.div<{ topMargin: number }>`
|
||||||
|
background: ${(props) => props.theme.noisyBackground};
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
flex-direction: row;
|
||||||
|
gap: ${(props) => props.theme.spacing(2)};
|
||||||
|
height: calc(100% - ${(props) => props.topMargin}px);
|
||||||
|
|
||||||
|
padding-bottom: ${(props) => props.theme.spacing(3)};
|
||||||
|
padding-right: ${(props) => props.theme.spacing(3)};
|
||||||
|
width: calc(100% - ${(props) => props.theme.spacing(3)});
|
||||||
|
`;
|
||||||
|
|
||||||
|
type LeftContainerProps = {
|
||||||
|
isRightDrawerOpen?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const LeftContainer = styled.div<LeftContainerProps>`
|
||||||
|
display: flex;
|
||||||
|
position: relative;
|
||||||
|
width: calc(
|
||||||
|
100% -
|
||||||
|
${(props) =>
|
||||||
|
props.isRightDrawerOpen
|
||||||
|
? `${props.theme.rightDrawerWidth} - ${props.theme.spacing(2)}`
|
||||||
|
: '0px'}
|
||||||
|
);
|
||||||
|
`;
|
||||||
|
|
||||||
|
export function ContentContainer({ children, topMargin }: OwnProps) {
|
||||||
|
const [isRightDrawerOpen] = useRecoilState(isRightDrawerOpenState);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MainContainer topMargin={topMargin ?? 0}>
|
||||||
|
<LeftContainer isRightDrawerOpen={isRightDrawerOpen}>
|
||||||
|
<Panel>{children}</Panel>
|
||||||
|
</LeftContainer>
|
||||||
|
<RightDrawer />
|
||||||
|
</MainContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
21
front/src/modules/ui/layout/containers/NoTopBarContainer.tsx
Normal file
21
front/src/modules/ui/layout/containers/NoTopBarContainer.tsx
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
|
import { ContentContainer } from './ContentContainer';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
children: JSX.Element;
|
||||||
|
};
|
||||||
|
|
||||||
|
const StyledContainer = styled.div`
|
||||||
|
display: flex;
|
||||||
|
padding-top: ${(props) => props.theme.spacing(4)};
|
||||||
|
width: 100%;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export function NoTopBarContainer({ children }: OwnProps) {
|
||||||
|
return (
|
||||||
|
<StyledContainer>
|
||||||
|
<ContentContainer topMargin={16}>{children}</ContentContainer>
|
||||||
|
</StyledContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -1,12 +1,10 @@
|
|||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { useRecoilState } from 'recoil';
|
|
||||||
|
|
||||||
import { Panel } from '../Panel';
|
|
||||||
import { RightDrawer } from '../right-drawer/components/RightDrawer';
|
|
||||||
import { isRightDrawerOpenState } from '../right-drawer/states/isRightDrawerOpenState';
|
|
||||||
import { TOP_BAR_MIN_HEIGHT, TopBar } from '../top-bar/TopBar';
|
import { TOP_BAR_MIN_HEIGHT, TopBar } from '../top-bar/TopBar';
|
||||||
|
|
||||||
|
import { ContentContainer } from './ContentContainer';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
children: JSX.Element;
|
children: JSX.Element;
|
||||||
title: string;
|
title: string;
|
||||||
@ -20,53 +18,18 @@ const StyledContainer = styled.div`
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const MainContainer = styled.div`
|
|
||||||
background: ${(props) => props.theme.noisyBackground};
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
gap: ${(props) => props.theme.spacing(2)};
|
|
||||||
height: calc(
|
|
||||||
100% - ${TOP_BAR_MIN_HEIGHT} - ${(props) => props.theme.spacing(2)} -
|
|
||||||
${(props) => props.theme.spacing(5)}
|
|
||||||
);
|
|
||||||
padding-bottom: ${(props) => props.theme.spacing(3)};
|
|
||||||
padding-right: ${(props) => props.theme.spacing(3)};
|
|
||||||
width: calc(100% - ${(props) => props.theme.spacing(3)});
|
|
||||||
`;
|
|
||||||
|
|
||||||
type LeftContainerProps = {
|
|
||||||
isRightDrawerOpen?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
const LeftContainer = styled.div<LeftContainerProps>`
|
|
||||||
display: flex;
|
|
||||||
position: relative;
|
|
||||||
width: calc(
|
|
||||||
100% -
|
|
||||||
${(props) =>
|
|
||||||
props.isRightDrawerOpen
|
|
||||||
? `${props.theme.rightDrawerWidth} - ${props.theme.spacing(2)}`
|
|
||||||
: '0px'}
|
|
||||||
);
|
|
||||||
`;
|
|
||||||
|
|
||||||
export function WithTopBarContainer({
|
export function WithTopBarContainer({
|
||||||
children,
|
children,
|
||||||
title,
|
title,
|
||||||
icon,
|
icon,
|
||||||
onAddButtonClick,
|
onAddButtonClick,
|
||||||
}: OwnProps) {
|
}: OwnProps) {
|
||||||
const [isRightDrawerOpen] = useRecoilState(isRightDrawerOpenState);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledContainer>
|
<StyledContainer>
|
||||||
<TopBar title={title} icon={icon} onAddButtonClick={onAddButtonClick} />
|
<TopBar title={title} icon={icon} onAddButtonClick={onAddButtonClick} />
|
||||||
<MainContainer>
|
<ContentContainer topMargin={TOP_BAR_MIN_HEIGHT}>
|
||||||
<LeftContainer isRightDrawerOpen={isRightDrawerOpen}>
|
{children}
|
||||||
<Panel>{children}</Panel>
|
</ContentContainer>
|
||||||
</LeftContainer>
|
|
||||||
<RightDrawer />
|
|
||||||
</MainContainer>
|
|
||||||
</StyledContainer>
|
</StyledContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,6 +8,7 @@ const StyledNavItemsContainer = styled.div`
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
margin-top: 40px;
|
margin-top: 40px;
|
||||||
|
min-width: 220px;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
function NavItemsContainer({ children }: OwnProps) {
|
function NavItemsContainer({ children }: OwnProps) {
|
||||||
|
|||||||
@ -4,10 +4,9 @@ import { useRecoilValue } from 'recoil';
|
|||||||
import { isNavbarOpenedState } from '../states/isNavbarOpenedState';
|
import { isNavbarOpenedState } from '../states/isNavbarOpenedState';
|
||||||
import { MOBILE_VIEWPORT } from '../styles/themes';
|
import { MOBILE_VIEWPORT } from '../styles/themes';
|
||||||
|
|
||||||
const StyledNavbarContainer = styled.div<{ width: string }>`
|
const StyledNavbarContainer = styled.div`
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
width: ${(props) =>
|
width: ${(props) => (useRecoilValue(isNavbarOpenedState) ? 'auto' : '0')};
|
||||||
useRecoilValue(isNavbarOpenedState) ? props.width : '0'};
|
|
||||||
padding: ${(props) => props.theme.spacing(2)};
|
padding: ${(props) => props.theme.spacing(2)};
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@ -19,18 +18,6 @@ const StyledNavbarContainer = styled.div<{ width: string }>`
|
|||||||
: '0'};
|
: '0'};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const NavbarSubContainer = styled.div`
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
margin-left: auto;
|
|
||||||
margin-top: 41px;
|
|
||||||
width: 160px;
|
|
||||||
|
|
||||||
@media (max-width: ${MOBILE_VIEWPORT}px) {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const NavbarContent = styled.div`
|
const NavbarContent = styled.div`
|
||||||
display: ${() => (useRecoilValue(isNavbarOpenedState) ? 'block' : 'none')};
|
display: ${() => (useRecoilValue(isNavbarOpenedState) ? 'block' : 'none')};
|
||||||
`;
|
`;
|
||||||
@ -44,18 +31,8 @@ export const NavbarContainer: React.FC<NavbarProps> = ({
|
|||||||
children,
|
children,
|
||||||
layout,
|
layout,
|
||||||
}) => {
|
}) => {
|
||||||
if (layout === 'secondary') {
|
|
||||||
return (
|
|
||||||
<StyledNavbarContainer width="500px">
|
|
||||||
<NavbarSubContainer>
|
|
||||||
<NavbarContent>{children}</NavbarContent>
|
|
||||||
</NavbarSubContainer>
|
|
||||||
</StyledNavbarContainer>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledNavbarContainer width="220px">
|
<StyledNavbarContainer>
|
||||||
<NavbarContent>{children}</NavbarContent>
|
<NavbarContent>{children}</NavbarContent>
|
||||||
</StyledNavbarContainer>
|
</StyledNavbarContainer>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import styled from '@emotion/styled';
|
|||||||
|
|
||||||
import { IconChevronLeft } from '@/ui/icons/index';
|
import { IconChevronLeft } from '@/ui/icons/index';
|
||||||
|
|
||||||
import NavCollapseButton from './NavCollapseButton';
|
import NavCollapseButton from '../NavCollapseButton';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
title: string;
|
title: string;
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
|
import NavBackButton from './NavBackButton';
|
||||||
|
|
||||||
|
type OwnProps = {
|
||||||
|
children: JSX.Element;
|
||||||
|
backButtonTitle: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const StyledContainer = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding-left: 300px;
|
||||||
|
padding-top: ${(props) => props.theme.spacing(6)};
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledNavItemsContainer = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default function SubNavbarContainer({
|
||||||
|
children,
|
||||||
|
backButtonTitle,
|
||||||
|
}: OwnProps) {
|
||||||
|
return (
|
||||||
|
<StyledContainer>
|
||||||
|
<NavBackButton title={backButtonTitle} />
|
||||||
|
<StyledNavItemsContainer>{children}</StyledNavItemsContainer>
|
||||||
|
</StyledContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -5,7 +5,7 @@ import { IconPlus } from '@/ui/icons/index';
|
|||||||
|
|
||||||
import NavCollapseButton from '../navbar/NavCollapseButton';
|
import NavCollapseButton from '../navbar/NavCollapseButton';
|
||||||
|
|
||||||
export const TOP_BAR_MIN_HEIGHT = '40px';
|
export const TOP_BAR_MIN_HEIGHT = 40;
|
||||||
|
|
||||||
const TopBarContainer = styled.div`
|
const TopBarContainer = styled.div`
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -14,7 +14,7 @@ const TopBarContainer = styled.div`
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
min-height: ${TOP_BAR_MIN_HEIGHT};
|
min-height: ${TOP_BAR_MIN_HEIGHT}px;
|
||||||
padding: ${(props) => props.theme.spacing(2)};
|
padding: ${(props) => props.theme.spacing(2)};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
@ -24,7 +24,6 @@ import { IconBuildingSkyscraper } from '@/ui/icons/index';
|
|||||||
import { IconList } from '@/ui/icons/index';
|
import { IconList } from '@/ui/icons/index';
|
||||||
import { WithTopBarContainer } from '@/ui/layout/containers/WithTopBarContainer';
|
import { WithTopBarContainer } from '@/ui/layout/containers/WithTopBarContainer';
|
||||||
import { BoolExpType } from '@/utils/interfaces/generic.interface';
|
import { BoolExpType } from '@/utils/interfaces/generic.interface';
|
||||||
import { AppPage } from '~/AppPage';
|
|
||||||
import { CompanyOrderByWithRelationInput as Companies_Order_By } from '~/generated/graphql';
|
import { CompanyOrderByWithRelationInput as Companies_Order_By } from '~/generated/graphql';
|
||||||
|
|
||||||
import { TableActionBarButtonCreateCommentThreadCompany } from './table/TableActionBarButtonCreateCommentThreadCompany';
|
import { TableActionBarButtonCreateCommentThreadCompany } from './table/TableActionBarButtonCreateCommentThreadCompany';
|
||||||
@ -76,31 +75,29 @@ export function Companies() {
|
|||||||
const companiesColumns = useCompaniesColumns();
|
const companiesColumns = useCompaniesColumns();
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
return (
|
return (
|
||||||
<AppPage>
|
<WithTopBarContainer
|
||||||
<WithTopBarContainer
|
title="Companies"
|
||||||
title="Companies"
|
icon={<IconBuildingSkyscraper size={theme.iconSizeMedium} />}
|
||||||
icon={<IconBuildingSkyscraper size={theme.iconSizeMedium} />}
|
onAddButtonClick={handleAddButtonClick}
|
||||||
onAddButtonClick={handleAddButtonClick}
|
>
|
||||||
>
|
<>
|
||||||
<>
|
<StyledCompaniesContainer>
|
||||||
<StyledCompaniesContainer>
|
<EntityTable
|
||||||
<EntityTable
|
data={companies}
|
||||||
data={companies}
|
columns={companiesColumns}
|
||||||
columns={companiesColumns}
|
viewName="All Companies"
|
||||||
viewName="All Companies"
|
viewIcon={<IconList size={16} />}
|
||||||
viewIcon={<IconList size={16} />}
|
availableSorts={availableSorts}
|
||||||
availableSorts={availableSorts}
|
availableFilters={availableFilters}
|
||||||
availableFilters={availableFilters}
|
onSortsUpdate={updateSorts}
|
||||||
onSortsUpdate={updateSorts}
|
onFiltersUpdate={updateFilters}
|
||||||
onFiltersUpdate={updateFilters}
|
/>
|
||||||
/>
|
</StyledCompaniesContainer>
|
||||||
</StyledCompaniesContainer>
|
<EntityTableActionBar>
|
||||||
<EntityTableActionBar>
|
<TableActionBarButtonCreateCommentThreadCompany />
|
||||||
<TableActionBarButtonCreateCommentThreadCompany />
|
<TableActionBarButtonDeleteCompanies />
|
||||||
<TableActionBarButtonDeleteCompanies />
|
</EntityTableActionBar>
|
||||||
</EntityTableActionBar>
|
</>
|
||||||
</>
|
</WithTopBarContainer>
|
||||||
</WithTopBarContainer>
|
|
||||||
</AppPage>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,11 +3,11 @@ import type { Meta } from '@storybook/react';
|
|||||||
import { userEvent, within } from '@storybook/testing-library';
|
import { userEvent, within } from '@storybook/testing-library';
|
||||||
|
|
||||||
import { graphqlMocks } from '~/testing/graphqlMocks';
|
import { graphqlMocks } from '~/testing/graphqlMocks';
|
||||||
|
import { getRenderWrapperForPage } from '~/testing/renderWrappers';
|
||||||
|
|
||||||
import { Companies } from '../Companies';
|
import { Companies } from '../Companies';
|
||||||
|
|
||||||
import { Story } from './Companies.stories';
|
import { Story } from './Companies.stories';
|
||||||
import { render } from './shared';
|
|
||||||
|
|
||||||
const meta: Meta<typeof Companies> = {
|
const meta: Meta<typeof Companies> = {
|
||||||
title: 'Pages/Companies/Comments',
|
title: 'Pages/Companies/Comments',
|
||||||
@ -17,7 +17,7 @@ const meta: Meta<typeof Companies> = {
|
|||||||
export default meta;
|
export default meta;
|
||||||
|
|
||||||
export const OpenCommentsSection: Story = {
|
export const OpenCommentsSection: Story = {
|
||||||
render,
|
render: getRenderWrapperForPage(<Companies />, '/companies'),
|
||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
|
|
||||||
|
|||||||
@ -2,10 +2,12 @@ import { expect } from '@storybook/jest';
|
|||||||
import type { Meta } from '@storybook/react';
|
import type { Meta } from '@storybook/react';
|
||||||
import { userEvent, within } from '@storybook/testing-library';
|
import { userEvent, within } from '@storybook/testing-library';
|
||||||
|
|
||||||
|
import { graphqlMocks } from '~/testing/graphqlMocks';
|
||||||
|
import { getRenderWrapperForPage } from '~/testing/renderWrappers';
|
||||||
|
|
||||||
import { Companies } from '../Companies';
|
import { Companies } from '../Companies';
|
||||||
|
|
||||||
import { Story } from './Companies.stories';
|
import { Story } from './Companies.stories';
|
||||||
import { mocks, render } from './shared';
|
|
||||||
|
|
||||||
const meta: Meta<typeof Companies> = {
|
const meta: Meta<typeof Companies> = {
|
||||||
title: 'Pages/Companies/FilterBy',
|
title: 'Pages/Companies/FilterBy',
|
||||||
@ -15,7 +17,7 @@ const meta: Meta<typeof Companies> = {
|
|||||||
export default meta;
|
export default meta;
|
||||||
|
|
||||||
export const FilterByName: Story = {
|
export const FilterByName: Story = {
|
||||||
render,
|
render: getRenderWrapperForPage(<Companies />, '/companies'),
|
||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
|
|
||||||
@ -38,12 +40,12 @@ export const FilterByName: Story = {
|
|||||||
expect(await canvas.findByText('Contains Air')).toBeInTheDocument();
|
expect(await canvas.findByText('Contains Air')).toBeInTheDocument();
|
||||||
},
|
},
|
||||||
parameters: {
|
parameters: {
|
||||||
msw: mocks,
|
msw: graphqlMocks,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FilterByAccountOwner: Story = {
|
export const FilterByAccountOwner: Story = {
|
||||||
render,
|
render: getRenderWrapperForPage(<Companies />, '/companies'),
|
||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
|
|
||||||
@ -72,6 +74,6 @@ export const FilterByAccountOwner: Story = {
|
|||||||
expect(await canvas.findByText('Is Charles Test')).toBeInTheDocument();
|
expect(await canvas.findByText('Is Charles Test')).toBeInTheDocument();
|
||||||
},
|
},
|
||||||
parameters: {
|
parameters: {
|
||||||
msw: mocks,
|
msw: graphqlMocks,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -2,10 +2,12 @@ import { expect } from '@storybook/jest';
|
|||||||
import type { Meta } from '@storybook/react';
|
import type { Meta } from '@storybook/react';
|
||||||
import { userEvent, within } from '@storybook/testing-library';
|
import { userEvent, within } from '@storybook/testing-library';
|
||||||
|
|
||||||
|
import { graphqlMocks } from '~/testing/graphqlMocks';
|
||||||
|
import { getRenderWrapperForPage } from '~/testing/renderWrappers';
|
||||||
|
|
||||||
import { Companies } from '../Companies';
|
import { Companies } from '../Companies';
|
||||||
|
|
||||||
import { Story } from './Companies.stories';
|
import { Story } from './Companies.stories';
|
||||||
import { mocks, render } from './shared';
|
|
||||||
|
|
||||||
const meta: Meta<typeof Companies> = {
|
const meta: Meta<typeof Companies> = {
|
||||||
title: 'Pages/Companies/SortBy',
|
title: 'Pages/Companies/SortBy',
|
||||||
@ -15,7 +17,7 @@ const meta: Meta<typeof Companies> = {
|
|||||||
export default meta;
|
export default meta;
|
||||||
|
|
||||||
export const SortByName: Story = {
|
export const SortByName: Story = {
|
||||||
render,
|
render: getRenderWrapperForPage(<Companies />, '/companies'),
|
||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
|
|
||||||
@ -41,6 +43,6 @@ export const SortByName: Story = {
|
|||||||
await expect(canvas.queryAllByTestId('remove-icon-name')).toStrictEqual([]);
|
await expect(canvas.queryAllByTestId('remove-icon-name')).toStrictEqual([]);
|
||||||
},
|
},
|
||||||
parameters: {
|
parameters: {
|
||||||
msw: mocks,
|
msw: graphqlMocks,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -15,7 +15,7 @@ export default meta;
|
|||||||
export type Story = StoryObj<typeof Companies>;
|
export type Story = StoryObj<typeof Companies>;
|
||||||
|
|
||||||
export const Default: Story = {
|
export const Default: Story = {
|
||||||
render: getRenderWrapperForPage(<Companies />),
|
render: getRenderWrapperForPage(<Companies />, '/companies'),
|
||||||
parameters: {
|
parameters: {
|
||||||
msw: graphqlMocks,
|
msw: graphqlMocks,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,57 +0,0 @@
|
|||||||
import { MemoryRouter } from 'react-router-dom';
|
|
||||||
import { ApolloProvider } from '@apollo/client';
|
|
||||||
import { graphql } from 'msw';
|
|
||||||
import { RecoilRoot } from 'recoil';
|
|
||||||
|
|
||||||
import { GraphqlQueryCompany } from '@/companies/interfaces/company.interface';
|
|
||||||
import { GraphqlQueryUser } from '@/users/interfaces/user.interface';
|
|
||||||
import { FullHeightStorybookLayout } from '~/testing/FullHeightStorybookLayout';
|
|
||||||
import { filterAndSortData } from '~/testing/mock-data';
|
|
||||||
import { mockedCompaniesData } from '~/testing/mock-data/companies';
|
|
||||||
import { mockedUsersData } from '~/testing/mock-data/users';
|
|
||||||
import { mockedClient } from '~/testing/mockedClient';
|
|
||||||
|
|
||||||
import { Companies } from '../Companies';
|
|
||||||
|
|
||||||
export const mocks = [
|
|
||||||
graphql.query('GetCompanies', (req, res, ctx) => {
|
|
||||||
const returnedMockedData = filterAndSortData<GraphqlQueryCompany>(
|
|
||||||
mockedCompaniesData,
|
|
||||||
req.variables.where,
|
|
||||||
req.variables.orderBy,
|
|
||||||
req.variables.limit,
|
|
||||||
);
|
|
||||||
return res(
|
|
||||||
ctx.data({
|
|
||||||
companies: returnedMockedData,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
graphql.query('SearchUser', (req, res, ctx) => {
|
|
||||||
const returnedMockedData = filterAndSortData<GraphqlQueryUser>(
|
|
||||||
mockedUsersData,
|
|
||||||
req.variables.where,
|
|
||||||
req.variables.orderBy,
|
|
||||||
req.variables.limit,
|
|
||||||
);
|
|
||||||
return res(
|
|
||||||
ctx.data({
|
|
||||||
searchResults: returnedMockedData,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
|
|
||||||
export function render() {
|
|
||||||
return (
|
|
||||||
<RecoilRoot>
|
|
||||||
<ApolloProvider client={mockedClient}>
|
|
||||||
<MemoryRouter>
|
|
||||||
<FullHeightStorybookLayout>
|
|
||||||
<Companies />
|
|
||||||
</FullHeightStorybookLayout>
|
|
||||||
</MemoryRouter>
|
|
||||||
</ApolloProvider>
|
|
||||||
</RecoilRoot>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,6 +1,5 @@
|
|||||||
import { IconTargetArrow } from '@/ui/icons/index';
|
import { IconTargetArrow } from '@/ui/icons/index';
|
||||||
import { WithTopBarContainer } from '@/ui/layout/containers/WithTopBarContainer';
|
import { WithTopBarContainer } from '@/ui/layout/containers/WithTopBarContainer';
|
||||||
import { AppPage } from '~/AppPage';
|
|
||||||
|
|
||||||
import { Board } from '../../modules/opportunities/components/Board';
|
import { Board } from '../../modules/opportunities/components/Board';
|
||||||
import { useBoard } from '../../modules/opportunities/hooks/useBoard';
|
import { useBoard } from '../../modules/opportunities/hooks/useBoard';
|
||||||
@ -13,10 +12,8 @@ export function Opportunities() {
|
|||||||
if (!initialBoard || !items)
|
if (!initialBoard || !items)
|
||||||
return <div>Initial board or items not found</div>;
|
return <div>Initial board or items not found</div>;
|
||||||
return (
|
return (
|
||||||
<AppPage>
|
<WithTopBarContainer title="Opportunities" icon={<IconTargetArrow />}>
|
||||||
<WithTopBarContainer title="Opportunities" icon={<IconTargetArrow />}>
|
<Board initialBoard={initialBoard} items={items} />
|
||||||
<Board initialBoard={initialBoard} items={items} />
|
</WithTopBarContainer>
|
||||||
</WithTopBarContainer>
|
|
||||||
</AppPage>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,7 +20,6 @@ import { EntityTable } from '@/ui/components/table/EntityTable';
|
|||||||
import { IconList, IconUser } from '@/ui/icons/index';
|
import { IconList, IconUser } from '@/ui/icons/index';
|
||||||
import { WithTopBarContainer } from '@/ui/layout/containers/WithTopBarContainer';
|
import { WithTopBarContainer } from '@/ui/layout/containers/WithTopBarContainer';
|
||||||
import { BoolExpType } from '@/utils/interfaces/generic.interface';
|
import { BoolExpType } from '@/utils/interfaces/generic.interface';
|
||||||
import { AppPage } from '~/AppPage';
|
|
||||||
|
|
||||||
import { TableActionBarButtonCreateCommentThreadPeople } from './table/TableActionBarButtonCreateCommentThreadPeople';
|
import { TableActionBarButtonCreateCommentThreadPeople } from './table/TableActionBarButtonCreateCommentThreadPeople';
|
||||||
import { TableActionBarButtonDeletePeople } from './table/TableActionBarButtonDeletePeople';
|
import { TableActionBarButtonDeletePeople } from './table/TableActionBarButtonDeletePeople';
|
||||||
@ -74,31 +73,29 @@ export function People() {
|
|||||||
|
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
return (
|
return (
|
||||||
<AppPage>
|
<WithTopBarContainer
|
||||||
<WithTopBarContainer
|
title="People"
|
||||||
title="People"
|
icon={<IconUser size={theme.iconSizeMedium} />}
|
||||||
icon={<IconUser size={theme.iconSizeMedium} />}
|
onAddButtonClick={handleAddButtonClick}
|
||||||
onAddButtonClick={handleAddButtonClick}
|
>
|
||||||
>
|
<>
|
||||||
<>
|
<StyledPeopleContainer>
|
||||||
<StyledPeopleContainer>
|
<EntityTable
|
||||||
<EntityTable
|
data={people}
|
||||||
data={people}
|
columns={peopleColumns}
|
||||||
columns={peopleColumns}
|
viewName="All People"
|
||||||
viewName="All People"
|
viewIcon={<IconList size={theme.iconSizeMedium} />}
|
||||||
viewIcon={<IconList size={theme.iconSizeMedium} />}
|
availableSorts={availableSorts}
|
||||||
availableSorts={availableSorts}
|
availableFilters={availableFilters}
|
||||||
availableFilters={availableFilters}
|
onSortsUpdate={updateSorts}
|
||||||
onSortsUpdate={updateSorts}
|
onFiltersUpdate={updateFilters}
|
||||||
onFiltersUpdate={updateFilters}
|
/>
|
||||||
/>
|
</StyledPeopleContainer>
|
||||||
</StyledPeopleContainer>
|
<EntityTableActionBar>
|
||||||
<EntityTableActionBar>
|
<TableActionBarButtonCreateCommentThreadPeople />
|
||||||
<TableActionBarButtonCreateCommentThreadPeople />
|
<TableActionBarButtonDeletePeople />
|
||||||
<TableActionBarButtonDeletePeople />
|
</EntityTableActionBar>
|
||||||
</EntityTableActionBar>
|
</>
|
||||||
</>
|
</WithTopBarContainer>
|
||||||
</WithTopBarContainer>
|
|
||||||
</AppPage>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,11 +3,11 @@ import type { Meta } from '@storybook/react';
|
|||||||
import { userEvent, within } from '@storybook/testing-library';
|
import { userEvent, within } from '@storybook/testing-library';
|
||||||
|
|
||||||
import { graphqlMocks } from '~/testing/graphqlMocks';
|
import { graphqlMocks } from '~/testing/graphqlMocks';
|
||||||
|
import { getRenderWrapperForPage } from '~/testing/renderWrappers';
|
||||||
|
|
||||||
import { People } from '../People';
|
import { People } from '../People';
|
||||||
|
|
||||||
import { Story } from './People.stories';
|
import { Story } from './People.stories';
|
||||||
import { render } from './shared';
|
|
||||||
|
|
||||||
const meta: Meta<typeof People> = {
|
const meta: Meta<typeof People> = {
|
||||||
title: 'Pages/People/Comments',
|
title: 'Pages/People/Comments',
|
||||||
@ -17,7 +17,7 @@ const meta: Meta<typeof People> = {
|
|||||||
export default meta;
|
export default meta;
|
||||||
|
|
||||||
export const OpenCommentsSection: Story = {
|
export const OpenCommentsSection: Story = {
|
||||||
render,
|
render: getRenderWrapperForPage(<People />, '/people'),
|
||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
|
|
||||||
|
|||||||
@ -3,11 +3,11 @@ import type { Meta } from '@storybook/react';
|
|||||||
import { userEvent, within } from '@storybook/testing-library';
|
import { userEvent, within } from '@storybook/testing-library';
|
||||||
|
|
||||||
import { graphqlMocks } from '~/testing/graphqlMocks';
|
import { graphqlMocks } from '~/testing/graphqlMocks';
|
||||||
|
import { getRenderWrapperForPage } from '~/testing/renderWrappers';
|
||||||
|
|
||||||
import { People } from '../People';
|
import { People } from '../People';
|
||||||
|
|
||||||
import { Story } from './People.stories';
|
import { Story } from './People.stories';
|
||||||
import { render } from './shared';
|
|
||||||
|
|
||||||
const meta: Meta<typeof People> = {
|
const meta: Meta<typeof People> = {
|
||||||
title: 'Pages/People/FilterBy',
|
title: 'Pages/People/FilterBy',
|
||||||
@ -17,7 +17,7 @@ const meta: Meta<typeof People> = {
|
|||||||
export default meta;
|
export default meta;
|
||||||
|
|
||||||
export const Email: Story = {
|
export const Email: Story = {
|
||||||
render,
|
render: getRenderWrapperForPage(<People />, '/people'),
|
||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
|
|
||||||
@ -44,7 +44,7 @@ export const Email: Story = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const CompanyName: Story = {
|
export const CompanyName: Story = {
|
||||||
render,
|
render: getRenderWrapperForPage(<People />, '/people'),
|
||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
|
|
||||||
|
|||||||
@ -7,12 +7,12 @@ import { GraphqlQueryCompany } from '@/companies/interfaces/company.interface';
|
|||||||
import { graphqlMocks } from '~/testing/graphqlMocks';
|
import { graphqlMocks } from '~/testing/graphqlMocks';
|
||||||
import { fetchOneFromData } from '~/testing/mock-data';
|
import { fetchOneFromData } from '~/testing/mock-data';
|
||||||
import { mockedPeopleData } from '~/testing/mock-data/people';
|
import { mockedPeopleData } from '~/testing/mock-data/people';
|
||||||
|
import { getRenderWrapperForPage } from '~/testing/renderWrappers';
|
||||||
import { sleep } from '~/testing/sleep';
|
import { sleep } from '~/testing/sleep';
|
||||||
|
|
||||||
import { People } from '../People';
|
import { People } from '../People';
|
||||||
|
|
||||||
import { Story } from './People.stories';
|
import { Story } from './People.stories';
|
||||||
import { render } from './shared';
|
|
||||||
|
|
||||||
const meta: Meta<typeof People> = {
|
const meta: Meta<typeof People> = {
|
||||||
title: 'Pages/People/Input',
|
title: 'Pages/People/Input',
|
||||||
@ -22,7 +22,7 @@ const meta: Meta<typeof People> = {
|
|||||||
export default meta;
|
export default meta;
|
||||||
|
|
||||||
export const InteractWithManyRows: Story = {
|
export const InteractWithManyRows: Story = {
|
||||||
render,
|
render: getRenderWrapperForPage(<People />, '/people'),
|
||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
|
|
||||||
@ -66,7 +66,7 @@ export const InteractWithManyRows: Story = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const CheckCheckboxes: Story = {
|
export const CheckCheckboxes: Story = {
|
||||||
render,
|
render: getRenderWrapperForPage(<People />, '/people'),
|
||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
|
|
||||||
@ -97,7 +97,7 @@ export const CheckCheckboxes: Story = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const EditRelation: Story = {
|
export const EditRelation: Story = {
|
||||||
render,
|
render: getRenderWrapperForPage(<People />, '/people'),
|
||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
|
|
||||||
@ -153,7 +153,7 @@ export const EditRelation: Story = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const SelectRelationWithKeys: Story = {
|
export const SelectRelationWithKeys: Story = {
|
||||||
render,
|
render: getRenderWrapperForPage(<People />, '/people'),
|
||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
|
|
||||||
|
|||||||
@ -3,11 +3,11 @@ import type { Meta } from '@storybook/react';
|
|||||||
import { userEvent, within } from '@storybook/testing-library';
|
import { userEvent, within } from '@storybook/testing-library';
|
||||||
|
|
||||||
import { graphqlMocks } from '~/testing/graphqlMocks';
|
import { graphqlMocks } from '~/testing/graphqlMocks';
|
||||||
|
import { getRenderWrapperForPage } from '~/testing/renderWrappers';
|
||||||
|
|
||||||
import { People } from '../People';
|
import { People } from '../People';
|
||||||
|
|
||||||
import { Story } from './People.stories';
|
import { Story } from './People.stories';
|
||||||
import { render } from './shared';
|
|
||||||
|
|
||||||
const meta: Meta<typeof People> = {
|
const meta: Meta<typeof People> = {
|
||||||
title: 'Pages/People/SortBy',
|
title: 'Pages/People/SortBy',
|
||||||
@ -17,7 +17,7 @@ const meta: Meta<typeof People> = {
|
|||||||
export default meta;
|
export default meta;
|
||||||
|
|
||||||
export const Email: Story = {
|
export const Email: Story = {
|
||||||
render,
|
render: getRenderWrapperForPage(<People />, '/people'),
|
||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
|
|
||||||
@ -43,7 +43,7 @@ export const Email: Story = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Cancel: Story = {
|
export const Cancel: Story = {
|
||||||
render,
|
render: getRenderWrapperForPage(<People />, '/people'),
|
||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
|
|
||||||
|
|||||||
@ -1,11 +1,10 @@
|
|||||||
import type { Meta, StoryObj } from '@storybook/react';
|
import type { Meta, StoryObj } from '@storybook/react';
|
||||||
|
|
||||||
import { graphqlMocks } from '~/testing/graphqlMocks';
|
import { graphqlMocks } from '~/testing/graphqlMocks';
|
||||||
|
import { getRenderWrapperForPage } from '~/testing/renderWrappers';
|
||||||
|
|
||||||
import { People } from '../People';
|
import { People } from '../People';
|
||||||
|
|
||||||
import { render } from './shared';
|
|
||||||
|
|
||||||
const meta: Meta<typeof People> = {
|
const meta: Meta<typeof People> = {
|
||||||
title: 'Pages/People',
|
title: 'Pages/People',
|
||||||
component: People,
|
component: People,
|
||||||
@ -16,7 +15,7 @@ export default meta;
|
|||||||
export type Story = StoryObj<typeof People>;
|
export type Story = StoryObj<typeof People>;
|
||||||
|
|
||||||
export const Default: Story = {
|
export const Default: Story = {
|
||||||
render,
|
render: getRenderWrapperForPage(<People />, '/people'),
|
||||||
parameters: {
|
parameters: {
|
||||||
msw: graphqlMocks,
|
msw: graphqlMocks,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,22 +0,0 @@
|
|||||||
import { MemoryRouter } from 'react-router-dom';
|
|
||||||
import { ApolloProvider } from '@apollo/client';
|
|
||||||
import { RecoilRoot } from 'recoil';
|
|
||||||
|
|
||||||
import { FullHeightStorybookLayout } from '~/testing/FullHeightStorybookLayout';
|
|
||||||
import { mockedClient } from '~/testing/mockedClient';
|
|
||||||
|
|
||||||
import { People } from '../People';
|
|
||||||
|
|
||||||
export function render() {
|
|
||||||
return (
|
|
||||||
<RecoilRoot>
|
|
||||||
<ApolloProvider client={mockedClient}>
|
|
||||||
<MemoryRouter>
|
|
||||||
<FullHeightStorybookLayout>
|
|
||||||
<People />
|
|
||||||
</FullHeightStorybookLayout>
|
|
||||||
</MemoryRouter>
|
|
||||||
</ApolloProvider>
|
|
||||||
</RecoilRoot>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,14 +1,20 @@
|
|||||||
|
import styled from '@emotion/styled';
|
||||||
import { useRecoilValue } from 'recoil';
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
import { currentUserState } from '@/auth/states/currentUserState';
|
import { currentUserState } from '@/auth/states/currentUserState';
|
||||||
import { SettingsPage } from '@/settings/components/SettingsPage';
|
import { NoTopBarContainer } from '@/ui/layout/containers/NoTopBarContainer';
|
||||||
import { TopTitle } from '@/ui/layout/top-bar/TopTitle';
|
import { TopTitle } from '@/ui/layout/top-bar/TopTitle';
|
||||||
|
|
||||||
|
const StyledContainer = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: ${(props) => props.theme.spacing(8)};
|
||||||
|
`;
|
||||||
export function SettingsProfile() {
|
export function SettingsProfile() {
|
||||||
const currentUser = useRecoilValue(currentUserState);
|
const currentUser = useRecoilValue(currentUserState);
|
||||||
return (
|
return (
|
||||||
<SettingsPage>
|
<NoTopBarContainer>
|
||||||
<>
|
<StyledContainer>
|
||||||
<TopTitle title="Profile" />
|
<TopTitle title="Profile" />
|
||||||
<div>
|
<div>
|
||||||
<h5>Name</h5>
|
<h5>Name</h5>
|
||||||
@ -18,7 +24,7 @@ export function SettingsProfile() {
|
|||||||
<h5>Email</h5>
|
<h5>Email</h5>
|
||||||
<span>{currentUser?.email} </span>
|
<span>{currentUser?.email} </span>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</StyledContainer>
|
||||||
</SettingsPage>
|
</NoTopBarContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,10 @@
|
|||||||
import type { Meta, StoryObj } from '@storybook/react';
|
import type { Meta, StoryObj } from '@storybook/react';
|
||||||
|
|
||||||
import { graphqlMocks } from '~/testing/graphqlMocks';
|
import { graphqlMocks } from '~/testing/graphqlMocks';
|
||||||
|
import { getRenderWrapperForPage } from '~/testing/renderWrappers';
|
||||||
|
|
||||||
import { SettingsProfile } from '../SettingsProfile';
|
import { SettingsProfile } from '../SettingsProfile';
|
||||||
|
|
||||||
import { render } from './shared';
|
|
||||||
|
|
||||||
const meta: Meta<typeof SettingsProfile> = {
|
const meta: Meta<typeof SettingsProfile> = {
|
||||||
title: 'Pages/Settings/SettingsProfile',
|
title: 'Pages/Settings/SettingsProfile',
|
||||||
component: SettingsProfile,
|
component: SettingsProfile,
|
||||||
@ -16,7 +15,7 @@ export default meta;
|
|||||||
export type Story = StoryObj<typeof SettingsProfile>;
|
export type Story = StoryObj<typeof SettingsProfile>;
|
||||||
|
|
||||||
export const Default: Story = {
|
export const Default: Story = {
|
||||||
render,
|
render: getRenderWrapperForPage(<SettingsProfile />, '/settings/profile'),
|
||||||
parameters: {
|
parameters: {
|
||||||
msw: graphqlMocks,
|
msw: graphqlMocks,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,22 +0,0 @@
|
|||||||
import { MemoryRouter } from 'react-router-dom';
|
|
||||||
import { ApolloProvider } from '@apollo/client';
|
|
||||||
import { RecoilRoot } from 'recoil';
|
|
||||||
|
|
||||||
import { FullHeightStorybookLayout } from '~/testing/FullHeightStorybookLayout';
|
|
||||||
import { mockedClient } from '~/testing/mockedClient';
|
|
||||||
|
|
||||||
import { SettingsProfile } from '../SettingsProfile';
|
|
||||||
|
|
||||||
export function render() {
|
|
||||||
return (
|
|
||||||
<RecoilRoot>
|
|
||||||
<ApolloProvider client={mockedClient}>
|
|
||||||
<MemoryRouter>
|
|
||||||
<FullHeightStorybookLayout>
|
|
||||||
<SettingsProfile />
|
|
||||||
</FullHeightStorybookLayout>
|
|
||||||
</MemoryRouter>
|
|
||||||
</ApolloProvider>
|
|
||||||
</RecoilRoot>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -3,17 +3,27 @@ import { MemoryRouter } from 'react-router-dom';
|
|||||||
import { ApolloProvider } from '@apollo/client';
|
import { ApolloProvider } from '@apollo/client';
|
||||||
import { RecoilRoot } from 'recoil';
|
import { RecoilRoot } from 'recoil';
|
||||||
|
|
||||||
|
import { DefaultLayout } from '@/ui/layout/DefaultLayout';
|
||||||
|
import { AuthProvider } from '~/providers/AuthProvider';
|
||||||
|
|
||||||
import { ComponentStorybookLayout } from './ComponentStorybookLayout';
|
import { ComponentStorybookLayout } from './ComponentStorybookLayout';
|
||||||
import { FullHeightStorybookLayout } from './FullHeightStorybookLayout';
|
import { FullHeightStorybookLayout } from './FullHeightStorybookLayout';
|
||||||
import { mockedClient } from './mockedClient';
|
import { mockedClient } from './mockedClient';
|
||||||
|
|
||||||
export function getRenderWrapperForPage(children: React.ReactElement) {
|
export function getRenderWrapperForPage(
|
||||||
|
children: React.ReactElement,
|
||||||
|
currentPath: string,
|
||||||
|
) {
|
||||||
return function render() {
|
return function render() {
|
||||||
return (
|
return (
|
||||||
<RecoilRoot>
|
<RecoilRoot>
|
||||||
<ApolloProvider client={mockedClient}>
|
<ApolloProvider client={mockedClient}>
|
||||||
<MemoryRouter>
|
<MemoryRouter initialEntries={[currentPath]}>
|
||||||
<FullHeightStorybookLayout>{children}</FullHeightStorybookLayout>
|
<FullHeightStorybookLayout>
|
||||||
|
<AuthProvider>
|
||||||
|
<DefaultLayout>{children}</DefaultLayout>
|
||||||
|
</AuthProvider>
|
||||||
|
</FullHeightStorybookLayout>
|
||||||
</MemoryRouter>
|
</MemoryRouter>
|
||||||
</ApolloProvider>
|
</ApolloProvider>
|
||||||
</RecoilRoot>
|
</RecoilRoot>
|
||||||
|
|||||||
@ -12316,10 +12316,10 @@ msw-storybook-addon@^1.8.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-node-process "^1.0.1"
|
is-node-process "^1.0.1"
|
||||||
|
|
||||||
msw@^1.2.1:
|
msw@1.2.1:
|
||||||
version "1.2.2"
|
version "1.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/msw/-/msw-1.2.2.tgz#126c3150c07f651e97b24fbd405821f3aeaf9397"
|
resolved "https://registry.yarnpkg.com/msw/-/msw-1.2.1.tgz#9dd347583eeba5e5c7f33b54be5600a899dc61bd"
|
||||||
integrity sha512-GsW3PE/Es/a1tYThXcM8YHOZ1S1MtivcS3He/LQbbTCx3rbWJYCtWD5XXyJ53KlNPT7O1VI9sCW3xMtgFe8XpQ==
|
integrity sha512-bF7qWJQSmKn6bwGYVPXOxhexTCGD5oJSZg8yt8IBClxvo3Dx/1W0zqE1nX9BSWmzRsCKWfeGWcB/vpqV6aclpw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@mswjs/cookies" "^0.2.2"
|
"@mswjs/cookies" "^0.2.2"
|
||||||
"@mswjs/interceptors" "^0.17.5"
|
"@mswjs/interceptors" "^0.17.5"
|
||||||
|
|||||||
Reference in New Issue
Block a user