docusaurus-plugin-generate-schema-docs 1.5.4 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/__tests__/__fixtures__/static/schemas/battle-test-event.json +771 -0
  2. package/__tests__/__fixtures__/static/schemas/conditional-event.json +52 -0
  3. package/__tests__/__fixtures__/static/schemas/nested-conditional-event.json +50 -0
  4. package/__tests__/__snapshots__/generateEventDocs.anchor.test.js.snap +3 -2
  5. package/__tests__/__snapshots__/generateEventDocs.nested.test.js.snap +4 -2
  6. package/__tests__/__snapshots__/generateEventDocs.test.js.snap +3 -2
  7. package/__tests__/components/ConditionalRows.test.js +150 -0
  8. package/__tests__/components/ConnectorLines.visualRegression.test.js +93 -0
  9. package/__tests__/components/FoldableRows.test.js +7 -4
  10. package/__tests__/components/SchemaRows.test.js +31 -0
  11. package/__tests__/components/__snapshots__/ConnectorLines.visualRegression.test.js.snap +7 -0
  12. package/__tests__/generateEventDocs.anchor.test.js +7 -0
  13. package/__tests__/generateEventDocs.nested.test.js +7 -0
  14. package/__tests__/generateEventDocs.partials.test.js +134 -0
  15. package/__tests__/generateEventDocs.test.js +7 -0
  16. package/__tests__/helpers/buildExampleFromSchema.test.js +49 -0
  17. package/__tests__/helpers/schemaToExamples.test.js +75 -0
  18. package/__tests__/helpers/schemaToTableData.battleTest.test.js +704 -0
  19. package/__tests__/helpers/schemaToTableData.hierarchicalLines.test.js +190 -7
  20. package/__tests__/helpers/schemaToTableData.test.js +263 -2
  21. package/__tests__/helpers/validator.test.js +6 -6
  22. package/components/ConditionalRows.js +156 -0
  23. package/components/FoldableRows.js +88 -61
  24. package/components/PropertiesTable.js +1 -1
  25. package/components/PropertyRow.js +24 -8
  26. package/components/SchemaRows.css +115 -0
  27. package/components/SchemaRows.js +31 -4
  28. package/generateEventDocs.js +55 -37
  29. package/helpers/buildExampleFromSchema.js +11 -0
  30. package/helpers/choice-index-template.js +2 -1
  31. package/helpers/continuingLinesStyle.js +169 -0
  32. package/helpers/schema-doc-template.js +2 -5
  33. package/helpers/schema-processing.js +3 -0
  34. package/helpers/schemaToExamples.js +75 -2
  35. package/helpers/schemaToTableData.js +252 -26
  36. package/helpers/update-schema-ids.js +3 -3
  37. package/helpers/validator.js +7 -19
  38. package/package.json +3 -2
@@ -0,0 +1,156 @@
1
+ import React, { useState, useId } from 'react';
2
+ import SchemaRows from './SchemaRows';
3
+ import clsx from 'clsx';
4
+ import {
5
+ getContinuingLinesStyle,
6
+ getBracketLinesStyle,
7
+ mergeBackgroundStyles,
8
+ } from '../helpers/continuingLinesStyle';
9
+
10
+ /**
11
+ * Renders 'if/then/else' conditionals as a set of `<tr>` elements
12
+ * that integrate directly into the main table body.
13
+ *
14
+ * Structure:
15
+ * 1. Condition (if) rows: always visible, info-styled
16
+ * 2. Branch toggles (then/else): radio-style, foldable
17
+ */
18
+ export default function ConditionalRows({
19
+ row,
20
+ bracketEnds: parentBracketEnds,
21
+ }) {
22
+ const {
23
+ condition,
24
+ branches,
25
+ level = 0,
26
+ continuingLevels = [],
27
+ groupBrackets = [],
28
+ } = row;
29
+ const [activeBranch, setActiveBranch] = useState(0);
30
+ const radioGroupId = useId();
31
+
32
+ // Compute this group's own bracket and combine with any parent brackets.
33
+ // bracketIndex = total number of existing brackets, so each group gets a unique position.
34
+ const ownBracket = {
35
+ level,
36
+ bracketIndex: groupBrackets.length,
37
+ };
38
+ const allBrackets = [...groupBrackets, ownBracket];
39
+
40
+ // colSpan=5 rows need continuing ancestor lines to pass through (for visual
41
+ // continuity). They don't have ::before/::after connectors, so we draw all
42
+ // ancestor lines via background gradients. We always include the immediate
43
+ // parent level (level-1) so the parent-to-child connector passes through.
44
+ // Only draw ancestor lines where the next level up is also continuing;
45
+ // this matches PropertyRow's filter to avoid stray lines at a parent's
46
+ // corner connector when that parent is the last in its group.
47
+ const ancestorLevels = continuingLevels.filter(
48
+ (lvl) => lvl < level && continuingLevels.includes(lvl + 1),
49
+ );
50
+ // Always include the immediate parent level so the parent-to-child
51
+ // connector passes through these full-width rows.
52
+ if (level > 0 && !ancestorLevels.includes(level - 1)) {
53
+ ancestorLevels.push(level - 1);
54
+ }
55
+ const treePassthrough = getContinuingLinesStyle(ancestorLevels, 0);
56
+ const indent = { paddingLeft: `${level * 1.25 + 0.5}rem` };
57
+
58
+ // If header: top cap on ownBracket
59
+ const headerBracketStyle = getBracketLinesStyle(allBrackets, {
60
+ starting: [ownBracket],
61
+ });
62
+ const headerMerged = mergeBackgroundStyles(
63
+ treePassthrough,
64
+ headerBracketStyle,
65
+ );
66
+ const headerStyle = { ...headerMerged, ...indent, paddingTop: '0.5rem' };
67
+
68
+ // Middle rows (branch toggles): no caps
69
+ const middleBracketStyle = getBracketLinesStyle(allBrackets);
70
+ const middleMerged = mergeBackgroundStyles(
71
+ treePassthrough,
72
+ middleBracketStyle,
73
+ );
74
+ const middleStyle = { ...middleMerged, ...indent };
75
+
76
+ // Last branch toggle when content is NOT shown: bottom cap (+ parent ends)
77
+ const allEndings = [ownBracket, ...(parentBracketEnds || [])];
78
+ const lastToggleBracketStyle = getBracketLinesStyle(allBrackets, {
79
+ ending: allEndings,
80
+ });
81
+ const lastToggleMerged = mergeBackgroundStyles(
82
+ treePassthrough,
83
+ lastToggleBracketStyle,
84
+ );
85
+ const lastToggleStyle = {
86
+ ...lastToggleMerged,
87
+ ...indent,
88
+ paddingBottom: '0.5rem',
89
+ };
90
+
91
+ return (
92
+ <>
93
+ {/* Condition (if) section - always visible */}
94
+ <tr className="conditional-condition-header">
95
+ <td colSpan={5} style={headerStyle}>
96
+ <span className="conditional-condition-label">
97
+ <span className="conditional-info-icon-wrapper">
98
+ <span className="conditional-info-icon">i</span>
99
+ <span className="conditional-info-tooltip">
100
+ The properties below define the condition. When the condition is
101
+ met, the &ldquo;Then&rdquo; branch applies. Otherwise, the
102
+ &ldquo;Else&rdquo; branch applies.
103
+ </span>
104
+ </span>
105
+ <strong>If</strong>
106
+ </span>
107
+ {condition.description && (
108
+ <p className="conditional-condition-description">
109
+ {condition.description}
110
+ </p>
111
+ )}
112
+ </td>
113
+ </tr>
114
+ <SchemaRows tableData={condition.rows} />
115
+
116
+ {/* Branch toggles (then / else) */}
117
+ {branches.map((branch, index) => {
118
+ const isActive = activeBranch === index;
119
+ const isLastBranch = index === branches.length - 1;
120
+ const toggleStyle =
121
+ isLastBranch && !isActive ? lastToggleStyle : middleStyle;
122
+ return (
123
+ <React.Fragment key={branch.title}>
124
+ <tr className="choice-row">
125
+ <td colSpan={5} style={toggleStyle}>
126
+ <label className="choice-row-header">
127
+ <input
128
+ type="radio"
129
+ name={`conditional-${radioGroupId}`}
130
+ checked={isActive}
131
+ onChange={() => setActiveBranch(index)}
132
+ />
133
+ <span className={clsx('choice-row-toggle', 'radio')} />
134
+ <strong>{branch.title}</strong>
135
+ </label>
136
+ {branch.description && (
137
+ <p className="choice-row-description">{branch.description}</p>
138
+ )}
139
+ </td>
140
+ </tr>
141
+ {isActive && (
142
+ <SchemaRows
143
+ tableData={branch.rows}
144
+ bracketEnds={
145
+ isLastBranch
146
+ ? [ownBracket, ...(parentBracketEnds || [])]
147
+ : undefined
148
+ }
149
+ />
150
+ )}
151
+ </React.Fragment>
152
+ );
153
+ })}
154
+ </>
155
+ );
156
+ }
@@ -1,60 +1,12 @@
1
- import React, { useState } from 'react';
1
+ import React, { useState, useId } from 'react';
2
2
  import SchemaRows from './SchemaRows';
3
3
  import Heading from '@theme/Heading';
4
4
  import clsx from 'clsx';
5
-
6
- /**
7
- * Generates inline styles for continuing hierarchical lines through a row.
8
- * @param {number[]} continuingLevels - Array of ancestor levels that need lines
9
- * @param {number} level - Current level of the choice
10
- * @returns {object} Style object with background gradients
11
- */
12
- const getContinuingLinesStyle = (continuingLevels = [], level = 0) => {
13
- const getLevelPosition = (lvl) => lvl * 1.25 + 0.5;
14
-
15
- const allGradients = [];
16
- const allSizes = [];
17
- const allPositions = [];
18
-
19
- // Draw continuing lines for all ancestor levels
20
- continuingLevels.forEach((lvl) => {
21
- const pos = getLevelPosition(lvl);
22
- allGradients.push(
23
- 'linear-gradient(var(--ifm-table-border-color), var(--ifm-table-border-color))',
24
- );
25
- allSizes.push('1px 100%');
26
- allPositions.push(`${pos}rem top`);
27
- });
28
-
29
- // Also draw the line for the immediate parent level (level - 1) if level > 0
30
- // This connects the choice rows to their parent property
31
- if (level > 0) {
32
- const parentPos = getLevelPosition(level - 1);
33
- // Check if this position is not already in continuing levels
34
- if (!continuingLevels.includes(level - 1)) {
35
- allGradients.push(
36
- 'linear-gradient(var(--ifm-table-border-color), var(--ifm-table-border-color))',
37
- );
38
- allSizes.push('1px 100%');
39
- allPositions.push(`${parentPos}rem top`);
40
- }
41
- }
42
-
43
- // Calculate indentation based on level
44
- const paddingLeft = `${level * 1.25 + 0.5}rem`;
45
-
46
- if (allGradients.length === 0) {
47
- return { paddingLeft };
48
- }
49
-
50
- return {
51
- paddingLeft,
52
- backgroundImage: allGradients.join(', '),
53
- backgroundSize: allSizes.join(', '),
54
- backgroundPosition: allPositions.join(', '),
55
- backgroundRepeat: 'no-repeat',
56
- };
57
- };
5
+ import {
6
+ getContinuingLinesStyle,
7
+ getBracketLinesStyle,
8
+ mergeBackgroundStyles,
9
+ } from '../helpers/continuingLinesStyle';
58
10
 
59
11
  // A clickable row that acts as a header/summary for a foldable choice
60
12
  const ChoiceRow = ({
@@ -89,7 +41,7 @@ const ChoiceRow = ({
89
41
  * Renders 'oneOf' and 'anyOf' choices as a set of foldable `<tr>` elements
90
42
  * that integrate directly into the main table body.
91
43
  */
92
- export default function FoldableRows({ row }) {
44
+ export default function FoldableRows({ row, bracketEnds: parentBracketEnds }) {
93
45
  const {
94
46
  choiceType,
95
47
  options,
@@ -98,7 +50,9 @@ export default function FoldableRows({ row }) {
98
50
  required,
99
51
  level = 0,
100
52
  continuingLevels = [],
53
+ groupBrackets = [],
101
54
  } = row;
55
+ const radioGroupId = useId();
102
56
  const [openOneOf, setOpenOneOf] = useState(0); // For oneOf, track the single open index
103
57
  const [openAnyOf, setOpenAnyOf] = useState({}); // For anyOf, track each option's state
104
58
 
@@ -120,14 +74,70 @@ export default function FoldableRows({ row }) {
120
74
  selectTitle
121
75
  );
122
76
 
123
- // Calculate continuing lines style for choice rows
124
- const continuingLinesStyle = getContinuingLinesStyle(continuingLevels, level);
77
+ // Compute this group's own bracket and combine with any parent brackets.
78
+ // bracketIndex = total number of existing brackets, so each group gets a unique position.
79
+ const ownBracket = {
80
+ level,
81
+ bracketIndex: groupBrackets.length,
82
+ };
83
+ const allBrackets = [...groupBrackets, ownBracket];
84
+
85
+ // colSpan=5 rows need continuing ancestor lines to pass through (for visual
86
+ // continuity). They don't have ::before/::after connectors, so we draw all
87
+ // ancestor lines via background gradients. We always include the immediate
88
+ // parent level (level-1) so the parent-to-child connector passes through.
89
+ // Only draw ancestor lines where the next level up is also continuing;
90
+ // this matches PropertyRow's filter to avoid stray lines at a parent's
91
+ // corner connector when that parent is the last in its group.
92
+ const ancestorLevels = continuingLevels.filter(
93
+ (lvl) => lvl < level && continuingLevels.includes(lvl + 1),
94
+ );
95
+ // Always include the immediate parent level so the parent-to-child
96
+ // connector passes through these full-width rows.
97
+ if (level > 0 && !ancestorLevels.includes(level - 1)) {
98
+ ancestorLevels.push(level - 1);
99
+ }
100
+ const treePassthrough = getContinuingLinesStyle(ancestorLevels, 0);
101
+ const indent = { paddingLeft: `${level * 1.25 + 0.5}rem` };
102
+
103
+ // Header row: top cap on the ownBracket
104
+ const headerBracketStyle = getBracketLinesStyle(allBrackets, {
105
+ starting: [ownBracket],
106
+ });
107
+ const headerMerged = mergeBackgroundStyles(
108
+ treePassthrough,
109
+ headerBracketStyle,
110
+ );
111
+ const headerStyle = { ...headerMerged, ...indent, paddingTop: '0.5rem' };
112
+
113
+ // Middle rows (option toggles): no caps
114
+ const middleBracketStyle = getBracketLinesStyle(allBrackets);
115
+ const middleMerged = mergeBackgroundStyles(
116
+ treePassthrough,
117
+ middleBracketStyle,
118
+ );
119
+ const middleStyle = { ...middleMerged, ...indent };
120
+
121
+ // Last toggle when its content is NOT shown: bottom cap on ownBracket (+ parent ends)
122
+ const allEndings = [ownBracket, ...(parentBracketEnds || [])];
123
+ const lastToggleBracketStyle = getBracketLinesStyle(allBrackets, {
124
+ ending: allEndings,
125
+ });
126
+ const lastToggleMerged = mergeBackgroundStyles(
127
+ treePassthrough,
128
+ lastToggleBracketStyle,
129
+ );
130
+ const lastToggleStyle = {
131
+ ...lastToggleMerged,
132
+ ...indent,
133
+ paddingBottom: '0.5rem',
134
+ };
125
135
 
126
136
  return (
127
137
  <>
128
138
  {/* A header row for the entire choice block */}
129
139
  <tr>
130
- <td colSpan={5} style={continuingLinesStyle}>
140
+ <td colSpan={5} style={headerStyle}>
131
141
  <Heading as="h4" className="choice-row-header-headline">
132
142
  {header}
133
143
  </Heading>
@@ -139,6 +149,10 @@ export default function FoldableRows({ row }) {
139
149
  {options.map((option, index) => {
140
150
  const isActive =
141
151
  choiceType === 'oneOf' ? openOneOf === index : !!openAnyOf[index];
152
+ const isLastOption = index === options.length - 1;
153
+ // Last toggle needs bottom cap when its content is hidden
154
+ const toggleStyle =
155
+ isLastOption && !isActive ? lastToggleStyle : middleStyle;
142
156
  return (
143
157
  <React.Fragment key={option.title}>
144
158
  <ChoiceRow
@@ -151,11 +165,24 @@ export default function FoldableRows({ row }) {
151
165
  }
152
166
  isActive={isActive}
153
167
  isRadio={choiceType === 'oneOf'}
154
- name={choiceType === 'oneOf' ? row.title : option.title}
155
- continuingLinesStyle={continuingLinesStyle}
168
+ name={
169
+ choiceType === 'oneOf'
170
+ ? `oneOf-${radioGroupId}`
171
+ : `anyOf-${option.title}-${radioGroupId}`
172
+ }
173
+ continuingLinesStyle={toggleStyle}
156
174
  />
157
175
  {/* If the option is active, render its rows directly into the table body */}
158
- {isActive && <SchemaRows tableData={option.rows} />}
176
+ {isActive && (
177
+ <SchemaRows
178
+ tableData={option.rows}
179
+ bracketEnds={
180
+ isLastOption
181
+ ? [ownBracket, ...(parentBracketEnds || [])]
182
+ : undefined
183
+ }
184
+ />
185
+ )}
159
186
  </React.Fragment>
160
187
  );
161
188
  })}
@@ -21,7 +21,7 @@ export default function PropertiesTable({ schema }) {
21
21
  isEnabled={isWordWrapOn}
22
22
  />
23
23
  </div>
24
- <table>
24
+ <table className="schema-table">
25
25
  <TableHeader />
26
26
  <tbody>
27
27
  <SchemaRows tableData={tableData} />
@@ -1,6 +1,7 @@
1
1
  import React from 'react';
2
2
  import clsx from 'clsx';
3
3
  import CodeBlock from '@theme/CodeBlock';
4
+ import { getBracketLinesStyle } from '../helpers/continuingLinesStyle';
4
5
 
5
6
  /**
6
7
  * Formats a single example value into a string for display in a CodeBlock.
@@ -30,7 +31,7 @@ const getContainerSymbol = (containerType) => {
30
31
  * All data is passed in via the `row` prop, which comes from `tableData`.
31
32
  * This component handles multi-row constraints using `rowSpan`.
32
33
  */
33
- export default function PropertyRow({ row, isLastInGroup }) {
34
+ export default function PropertyRow({ row, isLastInGroup, bracketEnds }) {
34
35
  const {
35
36
  name,
36
37
  level,
@@ -42,6 +43,7 @@ export default function PropertyRow({ row, isLastInGroup }) {
42
43
  hasChildren,
43
44
  containerType,
44
45
  continuingLevels = [],
46
+ groupBrackets = [],
45
47
  } = row;
46
48
 
47
49
  const indentStyle = {
@@ -56,7 +58,7 @@ export default function PropertyRow({ row, isLastInGroup }) {
56
58
  ? constraints
57
59
  : [null];
58
60
 
59
- // Generate background gradients for:
61
+ // Generate background gradients for tree connector lines (left side):
60
62
  // 1. Parent-to-child line (from 50% down) - for elements with children
61
63
  // 2. Continuing ancestor lines (full height) - for ancestors that have more siblings below
62
64
  //
@@ -69,7 +71,6 @@ export default function PropertyRow({ row, isLastInGroup }) {
69
71
  const allPositions = [];
70
72
 
71
73
  // Parent-to-child line: draws from 50% (below symbol) to bottom of cell
72
- // Position is at the child's level, which is (level * 20 + 8)px
73
74
  if (hasChildren) {
74
75
  const childLevelPos = level * 1.25 + 0.5;
75
76
  allGradients.push(
@@ -79,10 +80,14 @@ export default function PropertyRow({ row, isLastInGroup }) {
79
80
  allPositions.push(`${childLevelPos}rem top`);
80
81
  }
81
82
 
82
- // Continuing ancestor lines: full-height vertical lines for ancestors with more siblings
83
- // Filter to only include levels less than (level - 1) since ::before handles immediate parent
83
+ // Continuing ancestor lines: full-height vertical lines for ancestors with more siblings.
84
+ // Only levels strictly below the immediate parent (< level - 1) are drawn here.
85
+ // The ::before pseudo-element handles the immediate parent connector at level - 1.
86
+ // We also require lvl+1 to be in continuingLevels: if the next level up is not continuing
87
+ // (i.e. the parent was the last sibling), drawing lvl's line would create a stray line at
88
+ // the same x-position as that parent's elbow.
84
89
  continuingLevels
85
- .filter((lvl) => lvl < level - 1)
90
+ .filter((lvl) => lvl < level - 1 && continuingLevels.includes(lvl + 1))
86
91
  .forEach((lvl) => {
87
92
  const pos = getLevelPosition(lvl);
88
93
  allGradients.push(
@@ -102,11 +107,20 @@ export default function PropertyRow({ row, isLastInGroup }) {
102
107
  }
103
108
  : {};
104
109
 
110
+ // Bracket lines on the right side (description column)
111
+ const bracketCaps = bracketEnds ? { ending: bracketEnds } : undefined;
112
+ const bracketStyle = getBracketLinesStyle(groupBrackets, bracketCaps);
113
+
105
114
  const containerSymbol = getContainerSymbol(containerType);
106
115
 
107
116
  return (
108
117
  <>
109
- <tr className={clsx(required && 'required-row')}>
118
+ <tr
119
+ className={clsx(
120
+ required && 'required-row',
121
+ row.isCondition && 'conditional-condition-row',
122
+ )}
123
+ >
110
124
  <td
111
125
  rowSpan={rowSpan}
112
126
  style={{ ...indentStyle, ...continuingLinesStyle }}
@@ -159,7 +173,9 @@ export default function PropertyRow({ row, isLastInGroup }) {
159
173
  );
160
174
  })}
161
175
  </td>
162
- <td rowSpan={rowSpan}>{description || ''}</td>
176
+ <td rowSpan={rowSpan} style={bracketStyle}>
177
+ {description || ''}
178
+ </td>
163
179
  </tr>
164
180
 
165
181
  {/* Render subsequent constraints in their own rows */}
@@ -42,6 +42,40 @@
42
42
  margin-left: -0.55em;
43
43
  }
44
44
 
45
+ /* --- Schema Table Border Model --- */
46
+
47
+ /*
48
+ * Use border-collapse: separate with 0 spacing so that backgrounds are
49
+ * painted directly adjacent between rows — no collapsed border line is
50
+ * rendered on top of the background gradients. Horizontal separators are
51
+ * recreated via box-shadow so they don't interrupt vertical lines.
52
+ */
53
+ .schema-table {
54
+ border-collapse: separate;
55
+ border-spacing: 0;
56
+ border: 1px solid var(--ifm-table-border-color);
57
+ }
58
+
59
+ .schema-table th,
60
+ .schema-table td {
61
+ border: none;
62
+ border-left: 1px solid var(--ifm-table-border-color);
63
+ }
64
+
65
+ .schema-table th:first-child,
66
+ .schema-table td:first-child {
67
+ border-left: none;
68
+ }
69
+
70
+ .schema-table thead th {
71
+ border-bottom: 2px solid var(--ifm-table-border-color);
72
+ }
73
+
74
+ /* Row separators via box-shadow (doesn't mask background gradients) */
75
+ .schema-table tbody tr + tr td {
76
+ box-shadow: inset 0 1px 0 var(--ifm-table-border-color);
77
+ }
78
+
45
79
  /* --- Organigram Connector Line Styles --- */
46
80
 
47
81
  /*
@@ -225,6 +259,87 @@ td.level-6::after {
225
259
  transform: rotate(45deg);
226
260
  }
227
261
 
262
+ /* --- Conditional Row Styles --- */
263
+
264
+ .conditional-condition-header {
265
+ background-color: rgba(var(--ifm-color-info-rgb), 0.08);
266
+ }
267
+
268
+ .conditional-condition-header:hover {
269
+ background-color: rgba(var(--ifm-color-info-rgb), 0.12);
270
+ }
271
+
272
+ .conditional-condition-label {
273
+ display: flex;
274
+ align-items: center;
275
+ gap: 0.5rem;
276
+ }
277
+
278
+ .conditional-info-icon-wrapper {
279
+ position: relative;
280
+ display: inline-flex;
281
+ cursor: help;
282
+ }
283
+
284
+ .conditional-info-icon {
285
+ display: inline-flex;
286
+ align-items: center;
287
+ justify-content: center;
288
+ width: 1.25rem;
289
+ height: 1.25rem;
290
+ border-radius: 50%;
291
+ background-color: var(--ifm-color-info);
292
+ color: white;
293
+ font-size: 0.75rem;
294
+ font-weight: bold;
295
+ font-style: italic;
296
+ flex-shrink: 0;
297
+ }
298
+
299
+ .conditional-info-tooltip {
300
+ display: none;
301
+ position: absolute;
302
+ bottom: calc(100% + 0.5rem);
303
+ left: 0;
304
+ background-color: var(--ifm-color-emphasis-800);
305
+ color: var(--ifm-color-emphasis-0);
306
+ padding: 0.5rem 0.75rem;
307
+ border-radius: 0.375rem;
308
+ font-size: 0.8rem;
309
+ font-style: normal;
310
+ font-weight: normal;
311
+ line-height: 1.4;
312
+ white-space: normal;
313
+ width: max-content;
314
+ max-width: 280px;
315
+ z-index: 10;
316
+ pointer-events: none;
317
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
318
+ }
319
+
320
+ .conditional-info-tooltip::after {
321
+ content: '';
322
+ position: absolute;
323
+ top: 100%;
324
+ left: 0.625rem;
325
+ border: 0.375rem solid transparent;
326
+ border-top-color: var(--ifm-color-emphasis-800);
327
+ }
328
+
329
+ .conditional-info-icon-wrapper:hover .conditional-info-tooltip {
330
+ display: block;
331
+ }
332
+
333
+ .conditional-condition-description {
334
+ margin-left: 1.75rem;
335
+ font-size: 0.9em;
336
+ color: var(--ifm-color-secondary-darkest);
337
+ }
338
+
339
+ .conditional-condition-row {
340
+ background-color: rgba(var(--ifm-color-info-rgb), 0.04);
341
+ }
342
+
228
343
  /* --- Example CodeBlock Styles --- */
229
344
 
230
345
  .schema-examples {
@@ -2,26 +2,53 @@ import React from 'react';
2
2
  import './SchemaRows.css';
3
3
  import PropertyRow from './PropertyRow';
4
4
  import FoldableRows from './FoldableRows';
5
+ import ConditionalRows from './ConditionalRows';
5
6
 
6
7
  /**
7
8
  * Renders the rows of the schema table from a flat `tableData` array.
8
9
  * It maps over the array and renders the appropriate component for each row.
10
+ *
11
+ * @param {object} props
12
+ * @param {Array} props.tableData - Flat array of row objects
13
+ * @param {Array} [props.bracketEnds] - Bracket descriptors that end on the last row
9
14
  */
10
- export default function SchemaRows({ tableData }) {
15
+ export default function SchemaRows({ tableData, bracketEnds }) {
11
16
  if (!tableData) {
12
17
  return null;
13
18
  }
14
19
 
15
- return tableData.map((row) => {
20
+ return tableData.map((row, index) => {
16
21
  const key = row.path.join('.');
22
+ const isLast = index === tableData.length - 1;
17
23
 
18
24
  if (row.type === 'choice') {
19
- return <FoldableRows key={key} row={row} />;
25
+ return (
26
+ <FoldableRows
27
+ key={key}
28
+ row={row}
29
+ bracketEnds={isLast ? bracketEnds : undefined}
30
+ />
31
+ );
32
+ }
33
+
34
+ if (row.type === 'conditional') {
35
+ return (
36
+ <ConditionalRows
37
+ key={key}
38
+ row={row}
39
+ bracketEnds={isLast ? bracketEnds : undefined}
40
+ />
41
+ );
20
42
  }
21
43
 
22
44
  if (row.type === 'property') {
23
45
  return (
24
- <PropertyRow key={key} row={row} isLastInGroup={row.isLastInGroup} />
46
+ <PropertyRow
47
+ key={key}
48
+ row={row}
49
+ isLastInGroup={row.isLastInGroup}
50
+ bracketEnds={isLast ? bracketEnds : undefined}
51
+ />
25
52
  );
26
53
  }
27
54