Skip to content

Instantly share code, notes, and snippets.

@barthap
Created September 22, 2020 08:45
Show Gist options
  • Save barthap/362fc5609e7dc42d8e81f7e4cff18144 to your computer and use it in GitHub Desktop.
Save barthap/362fc5609e7dc42d8e81f7e4cff18144 to your computer and use it in GitHub Desktop.
Tabs component in React, works in mdx-js

Demo

tabs1

tabs2


Usage:

import { Tab, Tabs } from '~/components/plugins/Tabs';

// ...

<Tabs>
  <Tab label="First tab">First tab content</Tab>
  <Tab label="Second tab">Lorem ipsum</Tab>
</Tabs>

Implementation:

import { css } from 'emotion';
import React from 'react';

import * as Constants from '~/common/constants';

const STYLES_TAB_CONTAINER = css``;
const STYLES_TAB_CONTENT = css`
  padding: 10px 20px;
`;

const STYLES_TAB_LIST = css`
  border-bottom: 1px solid #ccc;
  padding-left: 0;
`;

const STYLES_TAB_LIST_ITEM = css`
  display: inline-block;
  list-style: none;
  margin-bottom: -1px;
  padding: 10px 15px;
  font-family: ${Constants.fontFamilies.demi};
  :hover {
    cursor: pointer;
  }
`;

const STYLES_TAB_LIST_ACTIVE = css`
  background-color: white;
  border: solid #ccc;
  border-bottom: 0px;
  border-width: 1px 1px 0 1px;
  background: ${Constants.expoColors.gray[200]};
  border-radius: 4px;
`;

const TabButton = ({ activeTab, label, onClick }) => {
  const handleClick = () => onClick(label);

  const classNames = [STYLES_TAB_LIST_ITEM];
  if (activeTab === label) {
    classNames.push(STYLES_TAB_LIST_ACTIVE);
  }

  return (
    <li className={classNames.join(' ')} onClick={handleClick}>
      {label}
    </li>
  );
};

/**
 * Dummy emelent, needed for `<Tabs/>` component to work properly
 */
export const Tab = ({ children }) => children;

/**
 * @example
 * <Tabs>
 *   <Tab label="Tab1">Tab 1 content...</Tab>
 *   <Tab label="Tab2">Tab 2 content...</Tab>
 * </Tabs>
 */
export const Tabs = ({ children }) => {
  const [activeTab, setActiveTab] = React.useState(children[0].props.label);

  return (
    <div className={STYLES_TAB_CONTAINER}>
      <ol className={STYLES_TAB_LIST}>
        {children.map(child => {
          const { label } = child.props;
          return (
            <TabButton activeTab={activeTab} key={label} label={label} onClick={setActiveTab} />
          );
        })}
      </ol>
      <div className={STYLES_TAB_CONTENT}>
        {children.map(child => {
          if (child.props.label !== activeTab) {
            return;
          }
          return child.props.children;
        })}
      </div>
    </div>
  );
};
@barthap
Copy link
Author

barthap commented Jan 15, 2022

Hi,

Try separating tags with double newlines, e.g.

<Tab>

<code>your code or other tags here</code>

</Tab>

Honestly, I don't know if it still works with MDX though. About a year ago it was used on this website for code examples, but then it most likely was updated in some way. You can see the website source code here, the page I linked is under pages/guides/authentication.md and the Tab component is under components/plugins/Tabs.js.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment