Skip to content

Commit

Permalink
Merge pull request #394 from beda-software/update-app-layout
Browse files Browse the repository at this point in the history
Update app layout
  • Loading branch information
ir4y authored Jan 21, 2025
2 parents 6972d0f + 0e18159 commit 77f5840
Show file tree
Hide file tree
Showing 93 changed files with 3,138 additions and 1,952 deletions.
170 changes: 170 additions & 0 deletions src/components/BaseLayout/PageContainer/PageContainer.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import { CalendarOutlined, MailOutlined, PhoneOutlined, PlusOutlined } from '@ant-design/icons';
import { Meta, StoryObj } from '@storybook/react';
import { Button, Input } from 'antd';
import styled from 'styled-components';

import { Table } from 'src/components/Table';
import { Tabs } from 'src/components/Tabs';
import { Text } from 'src/components/Typography';
import { withColorSchemeDecorator } from 'src/storybook/decorators';

import { PageContainer, PageContainerProps } from './index';

const content = (
<>
<Text>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. In suscipit magna sed pretium maximus. Duis
bibendum a lacus ut commodo. Nam eget justo tristique, tincidunt ligula vel, accumsan odio. Morbi purus
ante, bibendum vitae arcu eget, ultrices faucibus dolor. Sed fermentum blandit malesuada. Duis fringilla ac
tortor ut convallis. Fusce iaculis arcu dui. Ut non neque rhoncus, tincidunt ipsum in, lobortis magna. Donec
aliquet leo tellus. Proin pulvinar lacus sodales tortor eleifend rhoncus. Praesent varius maximus pulvinar.
</Text>
</>
);

const table = (
<Table
bordered
dataSource={[]}
columns={[
{
title: 'Patient',
dataIndex: 'patient',
key: 'patient',
width: '50%',
render: () => '-',
},
{
title: 'Practitioner',
dataIndex: 'practitioner',
key: 'practitioner',
width: '50%',
render: () => '-',
},
]}
/>
);

const tabs = (
<Tabs
boxShadow={false}
items={[
{
label: 'Tab',
key: 'tab1',
},
{
label: 'Tab',
key: 'tab2',
},
{
label: 'Tab',
key: 'tab3',
},
{
label: 'Tab',
key: 'tab4',
},
]}
activeKey={'tab1'}
/>
);

const S = {
Container: styled.div`
background-color: ${({ theme }) => theme.neutralPalette.gray_2};
`,
CustomRightColumn: styled.div`
display: flex;
align-items: center;
gap: 0 16px;
`,
Item: styled.div`
display: flex;
align-items: center;
gap: 0 4px;
color: ${({ theme }) => theme.neutral.secondaryText};
`,
};

const rightColumn1 = (
<S.CustomRightColumn>
<S.Item>
<CalendarOutlined />
<Text>05/12/1955 • 66 y.o.</Text>
</S.Item>
<S.Item>
<PhoneOutlined />
<Text>+972-222-3333</Text>
</S.Item>
<S.Item>
<MailOutlined />
<Text>cooper@gmail.com</Text>
</S.Item>
</S.CustomRightColumn>
);

const rightColumn2 = (
<>
<Input.Search placeholder="Search by email or phone" style={{ width: 328 }} />
<Button type="primary" icon={<PlusOutlined />}>
Add patient
</Button>
</>
);

const meta: Meta<typeof PageContainer> = {
title: 'Layout / PageContainer',
component: PageContainer,
// @ts-ignore
decorators: [withColorSchemeDecorator],
args: {
title: 'Patients',
children: content,
},
render: (args) => {
return (
<S.Container>
<PageContainer {...args} />
</S.Container>
);
},
};

export default meta;

type Story = StoryObj<PageContainerProps>;

export const Default: Story = {
args: {
title: 'Madison Cooper',
titleRightElement: rightColumn1,
},
};

export const WithTable: Story = {
args: {
layoutVariant: 'with-table',
children: table,
titleRightElement: rightColumn2,
},
};

export const WithTabs: Story = {
args: {
layoutVariant: 'with-tabs',
headerContent: tabs,
},
};

export const FullWidth: Story = {
args: {
maxWidth: '100%',
},
};

export const CustomWidth: Story = {
args: {
maxWidth: 500,
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { S } from "./styles";

export type PageContainerContentProps = React.HTMLAttributes<HTMLDivElement> & {
maxWidth?: number | string;
};

export function PageContainerContent(props: PageContainerContentProps) {
const { maxWidth, ...rest } = props;

return (
<S.PageContentContainer>
<S.PageContent {...rest} $maxWidth={maxWidth} />
</S.PageContentContainer>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import styled from 'styled-components';

import { maxWidthStyles } from '../PageContainerHeader/styles';

export const S = {
PageContentContainer: styled.div`
padding: 0 24px;
display: flex;
flex-direction: column;
align-items: center;
`,
PageContent: styled.div<{ $maxWidth?: number | string }>`
flex: 1;
display: flex;
flex-direction: column;
padding: 32px 0;
gap: 24px 0;
width: 100%;
${maxWidthStyles}
`,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { S } from "./styles";

export type PageContainerHeaderProps = React.HTMLAttributes<HTMLDivElement> & {
maxWidth?: number | string;
};

export function PageContainerHeader(props: PageContainerHeaderProps) {
const { maxWidth, ...rest } = props;

return (
<S.PageHeaderContainer>
<S.PageHeader {...rest} $maxWidth={maxWidth} />
</S.PageHeaderContainer>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { isNumber, isString } from 'lodash';
import styled, { css } from 'styled-components';

export const maxWidthStyles = css<{ $maxWidth?: number | string }>`
${({ $maxWidth }) => {
if (isNumber($maxWidth)) {
return css`
max-width: ${() => `${$maxWidth}px`};
`;
}
if (isString($maxWidth)) {
return css`
max-width: ${() => `${$maxWidth}`};
`;
}
return css`
max-width: 1080px;
`;
}}
`;

export const S = {
PageHeaderContainer: styled.div`
background-color: ${({ theme }) => theme.neutralPalette.gray_1};
display: flex;
flex-direction: column;
align-items: center;
padding: 0 24px;
`,
PageHeader: styled.div<{ $maxWidth?: number | string }>`
flex: 1;
display: flex;
flex-direction: column;
padding: 24px 0;
gap: 32px 0;
width: 100%;
${maxWidthStyles}
`,
};
82 changes: 82 additions & 0 deletions src/components/BaseLayout/PageContainer/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { S } from './styles';

export interface PageContainerProps {
/**
* The layout variant for the page.
* Options: 'default', 'with-table', 'with-tabs'.
*/
layoutVariant?: 'default' | 'with-table' | 'with-tabs';

/**
* Maximum width of the page header and content area.
* Accepts a number (in pixels) or a string (e.g., "100%").
*/
maxWidth?: number | string;

/**
* The main title of the page.
* Can be a string or any ReactNode for custom rendering.
*/
title?: React.ReactNode;

/**
* Content displayed to the left of the title.
* Example: a back button.
*/
titleLeftElement?: React.ReactNode;

/**
* Content displayed to the right of the title row.
* Example: an action button or user details.
*/
titleRightElement?: React.ReactNode;

/**
* Additional content displayed below the title in the header.
* Example: a search bar or tabs.
*/
headerContent?: React.ReactNode;

/**
* The main content of the page.
* Typically includes the primary content to render below the header.
*/
children?: React.ReactNode;
}

export function PageContainer(props: PageContainerProps = {}) {
const {
layoutVariant = 'default',
title,
headerContent,
children,
maxWidth,
titleLeftElement,
titleRightElement,
} = props;

return (
<>
<S.HeaderContainer maxWidth={maxWidth} $variant={layoutVariant}>
<S.Header>
<S.HeaderLeftColumn>
{titleLeftElement ? (
titleLeftElement
) : (
<>{title && <PageContainerTitle>{title}</PageContainerTitle>}</>
)}
</S.HeaderLeftColumn>
{titleRightElement && <S.HeaderRightColumn>{titleRightElement}</S.HeaderRightColumn>}
</S.Header>
{headerContent}
</S.HeaderContainer>
<S.ContentContainer $variant={layoutVariant} maxWidth={maxWidth}>
{children}
</S.ContentContainer>
</>
);
}

export function PageContainerTitle(props: React.HTMLAttributes<HTMLHeadingElement>) {
return <S.Title level={3} {...props} />;
}
Loading

0 comments on commit 77f5840

Please sign in to comment.