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

Commit a36274d

Browse files
committed
fix(plugins/plugin-client-common): nested choices in wizards were not presented as such
We weren't handling nested choice at all. As a result, the number of "Tasks" that Kui suggested the user needed to complete, both at the top level (in the progress bar in the wizard header) and also in the number of little squares in the given step, were both off.
1 parent f46d4b6 commit a36274d

File tree

8 files changed

+140
-31
lines changed

8 files changed

+140
-31
lines changed

plugins/plugin-client-common/src/components/Content/Markdown/components/Wizard/CodeBlockProps.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,17 @@ export default interface CodeBlockProps {
3030
* This option names the group, to keep it distinct from other
3131
* groups of choices.
3232
*/
33-
group?: string
33+
group: string
3434

3535
/**
3636
* This option names that member. e.g. if the user can choose
3737
* between doing either A-and-B or C-and-D, this identifies
3838
* whether we are part ofth e first choice (A+B) or the second
3939
* (C+D).
4040
*/
41-
member?: string
41+
member: number
42+
43+
/** Is this a nested choice? */
44+
nestingDepth: number
4245
}
4346
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import { WizardProps } from './rehype-wizard'
2121
import Progress from './Progress'
2222
import CodeBlockProps from './CodeBlockProps'
2323
import { onTabSwitch, offTabSwitch } from '../tabbed'
24-
import { OrderedGraph, blocks, compile, order, sequence } from '../code/graph'
24+
import { ChoicesMap, OrderedGraph, blocks, compile, order, sequence } from '../code/graph'
2525

2626
import Card from '../../../../spi/Card'
2727
import Icons from '../../../../spi/Icons'
@@ -52,7 +52,7 @@ export interface State {
5252
status: Record<string, Status>
5353

5454
/** Map from tab group to currently selected tab member */
55-
choices: Record<string, string>
55+
choices: ChoicesMap
5656
}
5757

5858
export default class Wizard extends React.PureComponent<Props, State> {

plugins/plugin-client-common/src/components/Content/Markdown/components/code/graph.ts

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import { ProgressStepState } from '../../../ProgressStepper'
3131
type Status = ProgressStepState['status']
3232

3333
/* map from choice group to selected choice member */
34-
type ChoicesMap = Record<CodeBlockProps['choice']['group'], CodeBlockProps['choice']['member']>
34+
export type ChoicesMap = Record<CodeBlockProps['choice']['group'], CodeBlockProps['choice']['member']>
3535

3636
/** Iteration order */
3737
export interface Ordered {
@@ -48,7 +48,7 @@ type Choice<T extends Unordered | Ordered = Unordered> = {
4848
group: string
4949

5050
choices: {
51-
member: string
51+
member: number
5252
graph: Sequence<T>
5353
}[]
5454
}
@@ -100,15 +100,24 @@ export function compile(blocks: CodeBlockProps[], ordering: 'sequence' | 'parall
100100

101101
let currentChoices: Choice
102102
let currentChoice: CodeBlockProps['choice']
103+
const currentChoiceStack: { currentChoice: CodeBlockProps['choice']; currentChoices: Choice }[] = []
103104

104105
const parts: Graph[] = []
105106

106107
blocks.forEach(block => {
107108
if (block.choice) {
108-
if (!currentChoice || currentChoice.group !== block.choice.group) {
109-
currentChoice = block.choice
109+
if (
110+
currentChoiceStack.length > 0 &&
111+
currentChoice.nestingDepth > 0 &&
112+
block.choice.nestingDepth < currentChoice.nestingDepth
113+
) {
114+
const tmp = currentChoiceStack.pop()
115+
currentChoice = tmp.currentChoice
116+
currentChoices = tmp.currentChoices
117+
}
110118

111-
currentChoices = {
119+
if (!currentChoice || currentChoice.group !== block.choice.group) {
120+
const newChoice = {
112121
group: block.choice.group,
113122
choices: [
114123
{
@@ -118,7 +127,21 @@ export function compile(blocks: CodeBlockProps[], ordering: 'sequence' | 'parall
118127
]
119128
}
120129

121-
parts.push(currentChoices)
130+
const isNested = currentChoice && block.choice.nestingDepth > 0 && block.choice.member === 0
131+
if (isNested) {
132+
currentChoiceStack.push({ currentChoice, currentChoices })
133+
currentChoices.choices.push({
134+
member: currentChoices.choices.length,
135+
graph: sequence([newChoice])
136+
})
137+
}
138+
139+
currentChoice = block.choice
140+
currentChoices = newChoice
141+
142+
if (!isNested) {
143+
parts.push(currentChoices)
144+
}
122145
} else if (currentChoice.member === block.choice.member) {
123146
// continuation of the current choice member
124147
currentChoices.choices[currentChoices.choices.length - 1].graph.sequence.push(block)

plugins/plugin-client-common/src/components/Content/Markdown/components/code/rehype-code-indexer.ts

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -90,23 +90,14 @@ export default function plugin(uuid: string) {
9090
reserialize()
9191
}
9292

93-
if (
94-
!attributes.cleanup &&
95-
!attributes.validate &&
96-
attributes.optional !== false &&
97-
isImplicitlyOptional(_)
98-
) {
99-
// don't propagate code blocks out of implicitly
100-
// optional elements, unless the code block has an
101-
// associated validator. The idea is that if the
102-
// author went through the trouble of writing a
103-
// validator for a code block, it probably isn't
104-
// optional. If the absence of a validator, then
105-
// we should stop propagating the code block
106-
// upwards if we reach some element that seems
107-
// indicative of blocking out an optional area,
108-
// e.g. an expandable section that is
109-
// default-closed
93+
if (attributes.optional === true || isImplicitlyOptional(_)) {
94+
// don't propagate code blocks out of either
95+
// explicitly or implicitly optional elements. Re:
96+
// implicitly, the idea is that we should stop
97+
// propagating the code block upwards if we reach
98+
// some element that seems indicative of blocking
99+
// out an optional area, e.g. an expandable section
100+
// that is default-closed
110101
attributes.optional = true
111102
reserialize()
112103
}
@@ -118,16 +109,19 @@ export default function plugin(uuid: string) {
118109
const parent = ancestors[idx - 1]
119110
if (isElementWithProperties(parent)) {
120111
const group = parent.properties['data-kui-choice-group']
121-
const member = String(_.properties['data-kui-tab-index'])
112+
const member = parseInt(_.properties['data-kui-tab-index'].toString(), 0)
113+
const nestingDepth = parseInt(parent.properties['data-kui-choice-nesting-depth'].toString(), 0)
122114

123115
if (!attributes.choice) {
124116
attributes.choice = {
125117
group,
126-
member
118+
member,
119+
nestingDepth
127120
}
128121
} else {
129122
attributes.group = group
130123
attributes.member = member
124+
attributes.nestingDepth = nestingDepth
131125
}
132126

133127
// re-serialize this update for later use

plugins/plugin-client-common/src/components/Content/Markdown/rehype-tabbed/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ export default function plugin(/* options */) {
4545
children: currentTabs,
4646
properties: {
4747
depth: tabStack.length,
48-
'data-kui-choice-group': v4()
48+
'data-kui-choice-group': v4(),
49+
'data-kui-choice-nesting-depth': tabStack.length
4950
}
5051
})
5152
}

plugins/plugin-client-common/src/test/core/markdown/wizards.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,37 @@ const IN4 = {
8383
{ name: 'CCC', body: 'CCCContent', description: '', codeBlocks: [{ index: 2, output: '333' }] }
8484
]
8585
}
86-
;[IN4, IN1, IN2, IN3].forEach(markdown => {
86+
87+
// nested choice
88+
const IN5 = {
89+
input: join(ROOT, 'tests/data/nested-choice1.md'),
90+
title: 'WizardTitle',
91+
description: 'WizardDescription',
92+
expectedSplitCount: 1,
93+
expectedCodeBlockTasks: 1,
94+
steps: [{ name: 'AAA', body: 'AAAContent', description: '', codeBlocks: [{ index: 0, output: 'XXX' }] }]
95+
}
96+
97+
// nested choice
98+
const IN6 = {
99+
input: join(ROOT, 'tests/data/nested-choice2.md'),
100+
title: 'WizardTitle',
101+
description: 'WizardDescription',
102+
expectedSplitCount: 1,
103+
expectedCodeBlockTasks: 2,
104+
steps: [
105+
{
106+
name: 'AAA',
107+
body: 'AAAContent',
108+
description: '',
109+
codeBlocks: [
110+
{ index: 0, output: 'XXX1' },
111+
{ index: 1, output: 'XXX2' }
112+
]
113+
}
114+
]
115+
}
116+
;[IN1, IN2, IN3, IN4, IN5, IN6].forEach(markdown => {
87117
describe(`wizards in markdown ${basename(markdown.input)} ${process.env.MOCHA_RUN_TARGET ||
88118
''}`, function(this: Common.ISuite) {
89119
before(Common.before(this))
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---
2+
layout: wizard
3+
---
4+
5+
# WizardTitle
6+
7+
WizardDescription
8+
9+
---
10+
11+
## AAA
12+
13+
AAAContent
14+
15+
=== "Tab11"
16+
17+
=== "Tab21"
18+
```bash
19+
echo XXX
20+
```
21+
22+
=== "Tab22"
23+
```bash
24+
echo YYY
25+
```
26+
27+
=== "Tab12"
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
---
2+
layout: wizard
3+
---
4+
5+
# WizardTitle
6+
7+
WizardDescription
8+
9+
---
10+
11+
## AAA
12+
13+
AAAContent
14+
15+
=== "Tab11"
16+
17+
=== "Tab21"
18+
```bash
19+
echo XXX1
20+
```
21+
22+
```bash
23+
echo XXX2
24+
```
25+
26+
=== "Tab22"
27+
```bash
28+
echo YYY
29+
```
30+
31+
=== "Tab12"

0 commit comments

Comments
 (0)