Skip to content
This repository was archived by the owner on Jul 30, 2025. It is now read-only.

Commit 45ceec9

Browse files
committed
feat(plugins/plugin-client-common): allow markdown to link to markdown tabs
i.e. activate a markdown tab as a result of clicking on a markdown link
1 parent d841cd1 commit 45ceec9

File tree

4 files changed

+132
-39
lines changed

4 files changed

+132
-39
lines changed

plugins/plugin-client-common/src/components/Content/Markdown/components/a.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { REPL, maybeKuiLink, pexecInCurrentTab } from '@kui-shell/core'
2020

2121
import { Props } from '../../Markdown'
2222
import { anchorFrom } from './heading'
23+
import { activateTab } from './tabbed'
2324

2425
// const LinkStatus = React.lazy(() => import('../../LinkStatus'))
2526
const Tooltip = React.lazy(() => import('../../../spi/Tooltip'))
@@ -50,10 +51,15 @@ export default function a(mdprops: Props, uuid: string, repl: REPL) {
5051
}
5152
}
5253
} else if (props.href.charAt(0) === '#') {
54+
// in case this is a reference to a section in the current kui tab
55+
// TODO: reference to a section in a different kui tab?
5356
const tab = mdprops.tab
5457
if (tab) {
5558
tab.show(`[data-markdown-anchor="${anchorFrom(uuid, props.href.slice(1))}"]`)
5659
}
60+
61+
// in case this is a reference to a markdown tab in this kui tab
62+
activateTab(props.href.slice(1), evt)
5763
} else if (file) {
5864
if ((isLocal || isNotebook) && !isAbsolute(file)) {
5965
// e.g. if a markdown has a relative reference to

plugins/plugin-client-common/src/components/Content/Markdown/components/tabbed.tsx

Lines changed: 83 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -17,32 +17,92 @@
1717
/* eslint-disable react/prop-types, react/display-name */
1818

1919
import React from 'react'
20-
import { Tab, Tabs, TabTitleText } from '@patternfly/react-core'
20+
import Slugger from 'github-slugger'
21+
import { EventEmitter } from 'events'
22+
import { Tab, Tabs, TabsProps, TabTitleText } from '@patternfly/react-core'
2123

2224
import Card from '../../../spi/Card'
2325

26+
type Props = {
27+
depth: number
28+
children: { props: { title: string; children?: React.ReactNode[] } }[]
29+
}
30+
31+
type State = Pick<TabsProps, 'activeKey'>
32+
33+
const activateEvents = new EventEmitter()
34+
export function activateTab(slug: string, evt?: React.MouseEvent) {
35+
activateEvents.emit(slug, evt)
36+
}
37+
38+
class LinkableTabs extends React.PureComponent<Props, State> {
39+
private readonly slugs = new Slugger()
40+
private readonly cleaners: (() => void)[] = []
41+
42+
public constructor(props: Props) {
43+
super(props)
44+
this.state = {
45+
activeKey: 0
46+
}
47+
48+
this.initEvents()
49+
}
50+
51+
private initEvents() {
52+
;(this.props.children || [])
53+
.map(_ => this.slugs.slug(_.props.title))
54+
.forEach((slug, activeKey) => {
55+
const onActivate = (evt?: React.MouseEvent) => {
56+
this.setState({ activeKey })
57+
58+
if (evt) {
59+
// prevent default interpretation of onClick for the href
60+
evt.preventDefault()
61+
}
62+
}
63+
activateEvents.on(slug, onActivate)
64+
this.cleaners.push(() => activateEvents.off(`/markdown/tabs/activate/${slug}`, onActivate))
65+
})
66+
}
67+
68+
public componentWillUnmount() {
69+
this.cleaners.forEach(_ => _())
70+
}
71+
72+
private readonly onSelect = (_, tabIndex: number | string) => {
73+
this.setState({
74+
activeKey: tabIndex
75+
})
76+
}
77+
78+
public render() {
79+
return (
80+
<Tabs
81+
className="kui--markdown-tabs paragraph"
82+
activeKey={this.state.activeKey}
83+
onSelect={this.onSelect}
84+
mountOnEnter
85+
unmountOnExit
86+
data-depth={this.props.depth}
87+
>
88+
{(this.props.children || []).map((_, idx) => (
89+
<Tab
90+
key={idx}
91+
eventKey={idx}
92+
data-depth={this.props.depth}
93+
data-title={_.props.title}
94+
className="kui--markdown-tab"
95+
title={<TabTitleText>{_.props.title}</TabTitleText>}
96+
>
97+
<Card className="kui--markdown-tab-card">{_.props && _.props.children}</Card>
98+
</Tab>
99+
))}
100+
</Tabs>
101+
)
102+
}
103+
}
104+
24105
export default function tabbed(props) {
25106
// isSecondary={parseInt(props.depth, 10) > 0}
26-
return (
27-
<Tabs
28-
className="kui--markdown-tabs paragraph"
29-
defaultActiveKey={0}
30-
mountOnEnter
31-
unmountOnExit
32-
data-depth={props.depth}
33-
>
34-
{(props.children || []).map((_, idx) => (
35-
<Tab
36-
key={idx}
37-
eventKey={idx}
38-
data-depth={props.depth}
39-
data-title={_.props.title}
40-
className="kui--markdown-tab"
41-
title={<TabTitleText>{_.props.title}</TabTitleText>}
42-
>
43-
<Card className="kui--markdown-tab-card">{_.props && _.props.children}</Card>
44-
</Tab>
45-
))}
46-
</Tabs>
47-
)
107+
return <LinkableTabs depth={props.depth}>{props.children}</LinkableTabs>
48108
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
=== "T1"
2+
tab1
3+
4+
=== "T2"
5+
6+
[activate T1](#t1)

plugins/plugin-client-common/web/scss/components/Terminal/Commentary.scss

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -24,20 +24,28 @@
2424
@import '../../Themes/mixins';
2525
@import 'BlockLinks';
2626

27-
body[kui-theme-style] .pf-c-content {
28-
color: inherit;
29-
30-
ol,
31-
ul {
32-
margin: 0;
27+
@mixin TextContent {
28+
.pf-c-content {
29+
@content;
3330
}
31+
}
3432

35-
/* odd bug in patternfly... */
36-
.pf-c-tabs__list li + li {
37-
margin-top: 0;
38-
}
39-
.pf-c-breadcrumb li + li {
40-
margin-top: 0;
33+
body[kui-theme-style] {
34+
@include TextContent {
35+
color: inherit;
36+
37+
ol,
38+
ul {
39+
margin: 0;
40+
}
41+
42+
/* odd bug in patternfly... */
43+
.pf-c-tabs__list li + li {
44+
margin-top: 0;
45+
}
46+
.pf-c-breadcrumb li + li {
47+
margin-top: 0;
48+
}
4149
}
4250
}
4351

@@ -339,6 +347,17 @@ body[kui-theme-style='dark'] {
339347
& > p:last-child {
340348
margin-bottom: 0;
341349
}
350+
351+
@include TextContent {
352+
& > .marked-content {
353+
& > p:first-child {
354+
margin-top: 0;
355+
}
356+
& > p:last-child {
357+
margin-bottom: 0;
358+
}
359+
}
360+
}
342361
}
343362
}
344363

@@ -361,10 +380,12 @@ body[kui-theme-style='dark'] {
361380
}
362381

363382
/** Markdown-rendered content not in a Commentary */
364-
pre > .pf-c-content {
365-
font-size: inherit;
366-
p {
367-
white-space: pre;
383+
pre {
384+
@include TextContent {
385+
font-size: inherit;
386+
p {
387+
white-space: pre;
388+
}
368389
}
369390
}
370391

0 commit comments

Comments
 (0)