Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(theme-classic): allow passing additional attributes to tab headings #6082

Merged
merged 5 commits into from
Dec 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion packages/docusaurus-theme-classic/src/theme-classic.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,7 @@ declare module '@theme/TabItem' {
readonly label?: string;
readonly hidden?: boolean;
readonly className?: string;
readonly attributes?: Record<string, unknown>;
}

const TabItem: (props: Props) => JSX.Element;
Expand All @@ -529,7 +530,11 @@ declare module '@theme/Tabs' {
readonly block?: boolean;
readonly children: readonly ReactElement<TabItemProps>[];
readonly defaultValue?: string | null;
readonly values?: readonly {value: string; label?: string}[];
readonly values?: readonly {
value: string;
label?: string;
attributes?: Record<string, unknown>;
}[];
readonly groupId?: string;
readonly className?: string;
}
Expand Down
24 changes: 18 additions & 6 deletions packages/docusaurus-theme-classic/src/theme/Tabs/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,13 @@ function TabsComponent(props: Props): JSX.Element {
);
});
const values =
valuesProp ?? children.map(({props: {value, label}}) => ({value, label}));
valuesProp ??
// We only pick keys that we recognize. MDX would inject some keys by default
children.map(({props: {value, label, attributes}}) => ({
value,
label,
attributes,
}));
const dup = duplicates(values, (a, b) => a.value === b.value);
if (dup.length > 0) {
throw new Error(
Expand Down Expand Up @@ -144,19 +150,25 @@ function TabsComponent(props: Props): JSX.Element {
},
className,
)}>
{values.map(({value, label}) => (
{values.map(({value, label, attributes}) => (
<li
role="tab"
tabIndex={selectedValue === value ? 0 : -1}
aria-selected={selectedValue === value}
className={clsx('tabs__item', styles.tabItem, {
'tabs__item--active': selectedValue === value,
})}
key={value}
ref={(tabControl) => tabRefs.push(tabControl)}
onKeyDown={handleKeydown}
onFocus={handleTabChange}
onClick={handleTabChange}>
onClick={handleTabChange}
{...attributes}
className={clsx(
'tabs__item',
styles.tabItem,
attributes?.className as string,
{
'tabs__item--active': selectedValue === value,
},
)}>
{label ?? value}
</li>
))}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

.red {
color: red;
}
.red[aria-selected="true"] {
border-bottom-color: red;
}

.orange {
color: orange;
}
.orange[aria-selected="true"] {
border-bottom-color: orange;
}

.yellow {
color: yellow;
}
.yellow[aria-selected="true"] {
border-bottom-color: yellow;
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ slug: /markdown-features/tabs
import BrowserWindow from '@site/src/components/BrowserWindow';
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import styles from './markdown-features-tabs-styles.module.css';
```

Docusaurus provides `<Tabs>` components that you can use thanks to [MDX](./markdown-features-react.mdx):
Expand Down Expand Up @@ -229,15 +230,12 @@ Tab choices with different `groupId`s will not interfere with each other:
You might want to customize the appearance of certain set of tabs. To do that you can pass the string in `className` prop and the specified CSS class will be added to the `Tabs` component:

```jsx
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

// highlight-next-line
<Tabs className="unique-tabs">
<TabItem value="Apple">This is an apple 🍎</TabItem>
<TabItem value="Orange">This is an orange 🍊</TabItem>
<TabItem value="Banana">This is a banana 🍌</TabItem>
</Tabs>;
</Tabs>
```

```mdx-code-block
Expand All @@ -249,3 +247,76 @@ import TabItem from '@theme/TabItem';
</Tabs>
</BrowserWindow>
```

### Customizing tab headings

You can also customize each tab heading independently by using the `attributes` field. The extra props can be passed to the headings either through the `values` prop in `Tabs`, or props of each `TabItem`—in the same way as you declare `label`.

<!-- prettier-ignore-start -->
```jsx title="some-doc.mdx"
import styles from './styles.module.css';

<Tabs>
<TabItem value="apple" label="Apple" attributes={{className: styles.red}}>
This is an apple 🍎
</TabItem>
<TabItem value="orange" label="Orange" attributes={{className: styles.orange}}>
This is an orange 🍊
</TabItem>
<TabItem value="banana" label="Banana" attributes={{className: styles.yellow}}>
This is a banana 🍌
</TabItem>
</Tabs>
```
<!-- prettier-ignore-end -->

```css title="styles.module.css"
.red {
color: red;
}
.red[aria-selected='true'] {
border-bottom-color: red;
}

.orange {
color: orange;
}
.orange[aria-selected='true'] {
border-bottom-color: orange;
}

.yellow {
color: yellow;
}
.yellow[aria-selected='true'] {
border-bottom-color: yellow;
}
```

```mdx-code-block
<BrowserWindow>
<Tabs>
<TabItem value="apple" label="Apple" attributes={{className: styles.red}}>
This is an apple 🍎
</TabItem>
<TabItem value="orange" label="Orange" attributes={{className: styles.orange}}>
This is an orange 🍊
</TabItem>
<TabItem value="banana" label="Banana" attributes={{className: styles.yellow}}>
This is a banana 🍌
</TabItem>
</Tabs>
</BrowserWindow>
```

:::tip

`className` would be merged with other default class names. You may also use a custom `data-value` field (`{'data-value': 'apple'}`) paired with CSS attribute selectors:

```css title="styles.module.css"
li[role='tab'][data-value='apple'] {
color: red;
}
```

:::
1 change: 1 addition & 0 deletions website/src/components/BrowserWindow/styles.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
border-top-left-radius: var(--ifm-global-radius);
border-top-right-radius: var(--ifm-global-radius);
box-shadow: var(--ifm-global-shadow-lw);
margin-bottom: var(--ifm-leading);
}

.browserWindowHeader {
Expand Down