Skip to content

Commit

Permalink
Merge pull request #37 from ebs-integrator/tabs
Browse files Browse the repository at this point in the history
tabs componets
  • Loading branch information
marcellefter authored Jan 27, 2021
2 parents d319ee5 + 77dcadf commit a41b2d9
Show file tree
Hide file tree
Showing 9 changed files with 212 additions and 1 deletion.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ebs-integrator/react-ebs-ui",
"version": "0.0.1-beta.12",
"version": "0.0.1-beta.13",
"description": "Basic React UI elements.",
"author": "EBS Integrator",
"maintainers": [
Expand Down
17 changes: 17 additions & 0 deletions src/components/atoms/Tabs/Panel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import * as React from 'react';
import cn from 'classnames';
import { useTabs } from './Tabs';

export interface PanelProps {
tabKey: string;
className?: string;
}

/**
* Individual panel component.
*/
export const Panel: React.FC<PanelProps> = ({ tabKey, className, children }) => {
const { activeTab } = useTabs();

return activeTab === tabKey ? <div className={cn(className)}>{children}</div> : null;
};
37 changes: 37 additions & 0 deletions src/components/atoms/Tabs/Tab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import * as React from 'react';
import cn from 'classnames';
import { useTabs } from './Tabs';

export interface TabProps {
tabKey: string;
label: React.ReactNode;
disabled?: boolean;
className?: string;
onClick?: (tabKey: string) => void;
}

/**
* This component allows changing of the active Tab.
*/
export const Tab: React.FC<TabProps> = ({ tabKey, disabled, label, className, onClick }) => {
const { activeTab, setActiveTab } = useTabs();

const handleClick = (): void => {
if (setActiveTab && !disabled) {
setActiveTab(tabKey);
}

if (onClick) {
onClick(tabKey);
}
};

return (
<div
onClick={handleClick}
className={cn(`ebs-tabs__item`, className, { active: activeTab === tabKey, disabled: disabled })}
>
{label}
</div>
);
};
25 changes: 25 additions & 0 deletions src/components/atoms/Tabs/Tabs.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
.ebs-tabs {
display: flex;
border: $tab-border;
border-bottom: $tab-border-bottom;

}

.ebs-tabs__item {
cursor: pointer;
margin: $tab-item-margin;
padding: $tab-item-padding;

&.active {
border-bottom: $tab-item-border-bottom-active;
}

&.disabled {
color: $tab-item-disabled-color;
cursor: initial;
}
}

.ebs-tabs__content {
padding: $tab-content-padding;
}
55 changes: 55 additions & 0 deletions src/components/atoms/Tabs/Tabs.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import * as React from 'react';
import { Tabs } from './Tabs';
import { exportStory } from 'libs';

export default {
title: exportStory('Tabs', 'atoms'),
component: Tabs,
};

export const regular = (): React.ReactElement => {
// eslint-disable-next-line react-hooks/rules-of-hooks
const [active, setActive] = React.useState('first');

const data = [
{
label: <span>First tab</span>,
key: 'first',
content: <span>Content First tab</span>,
},
{
label: <span>Second tab</span>,
disabled: true,
key: 'second',
content: <span>Content Second tab</span>,
},
{
label: <span>Third tab</span>,
disabled: false,
key: 'third',
content: <span>Content Third tab</span>,
},
{
label: <span>Fourth tab</span>,
disabled: true,
key: 'fourth',
content: <span>Content Fourth tab</span>,
},
];

return (
<div>
<Tabs activeTab={active} setActiveTab={setActive}>
{data.map((item) => (
<Tabs.Tab {...item} tabKey={item.key} />
))}
<h2>Custom elements for all tabs</h2>
{data.map((item) => (
<Tabs.Panel key={item.key} tabKey={item.key}>
{item.content}
</Tabs.Panel>
))}
</Tabs>
</div>
);
};
66 changes: 66 additions & 0 deletions src/components/atoms/Tabs/Tabs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import * as React from 'react';
import cn from 'classnames';
import { Panel, PanelProps } from './Panel';
import { Tab, TabProps } from './Tab';

export interface TabsProps {
Tab: React.FC<TabProps>;
Panel: React.FC<PanelProps>;
}

export interface TabsMainProps {
activeTab?: string;
setActiveTab?: (key: string) => void;
className?: string;
}

export interface TabsContext {
activeTab?: string;
setActiveTab?: (key: string) => void;
}

const TabsContext = React.createContext<TabsContext | undefined>(undefined);

const Tabs: React.FC<TabsMainProps> & TabsProps = ({ activeTab, setActiveTab, className, children }) => {
const memoizedContextValue = React.useMemo(
() => ({
activeTab,
setActiveTab,
}),
[activeTab],
);

return (
<TabsContext.Provider value={memoizedContextValue}>
<div className={cn(`ebs-tabs`, className)}>
{children &&
React.Children.map(children, (child) => {
if (child && child.type === Tab) {
return child;
}
})}
</div>
<div className={`ebs-tabs__content`}>
{children &&
React.Children.map(children, (child) => {
if (child && child.type !== Tab) {
return child;
}
})}
</div>
</TabsContext.Provider>
);
};

export const useTabs = (): TabsContext => {
const context = React.useContext(TabsContext);
if (!context) {
throw new Error('This component must be used within a <Tabs> component.');
}
return context;
};

Tabs.Tab = Tab;
Tabs.Panel = Panel;

export { Tabs };
1 change: 1 addition & 0 deletions src/components/atoms/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@ export { DetailsCard, HeadLeft } from './DetailsCard/DetailsCard';
export { Mask } from './Mask/Mask';
export { Container, Row, Col } from './Grid';
export { Collapse, CollapseGroup } from './Collapse/Collapse';
export { Tabs } from './Tabs/Tabs';

export { Animated } from './Animated';
1 change: 1 addition & 0 deletions src/components/atoms/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@
@import './Avatar/Avatar';
@import './Alert/Alert';
@import './Badge/Badge';
@import './Tabs/Tabs';
9 changes: 9 additions & 0 deletions src/styles/variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,15 @@ $scrollbar-thumb-hover-background-color: $primary-color !default;
// Icon
$icon-svg-color: $primary-color !default;

// Tabs
$tab-item-margin: 0 30px 0 0;
$tab-item-padding: 20px 0;
$tab-border: none;
$tab-border-bottom: 1px solid #111;
$tab-item-border-bottom-active: 2px solid $primary-color;
$tab-item-disabled-color: $normal-color;
$tab-content-padding: 10px;

// Form inputs
//
// Define base size & colors of scrollbar
Expand Down

0 comments on commit a41b2d9

Please sign in to comment.