@redpanda-data/docs-extensions-and-macros 4.7.0 β 4.7.2
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.
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
-
|
|
3
2
|
/**
|
|
4
3
|
* Registers macros for use in Redpanda Connect contexts in the Redpanda documentation.
|
|
5
4
|
* @param {Registry} registry - The Antora registry where this block macro is registered.
|
|
@@ -7,23 +6,31 @@
|
|
|
7
6
|
*/
|
|
8
7
|
module.exports.register = function (registry, context) {
|
|
9
8
|
function filterComponentTable() {
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
|
|
9
|
+
const nameInputElement = document.getElementById('componentTableSearch');
|
|
10
|
+
const nameInput = nameInputElement ? nameInputElement.value.trim().toLowerCase() : '';
|
|
11
|
+
const typeFilter = Array.from(document.querySelectorAll('#typeFilterMenu input[type="checkbox"]:checked')).map(checkbox => checkbox.value);
|
|
14
12
|
// Check for the existence of support and enterprise license filters (optional)
|
|
15
|
-
const supportFilterElement = document.querySelector('#
|
|
13
|
+
const supportFilterElement = document.querySelector('#supportFilterMenu');
|
|
16
14
|
const supportFilter = supportFilterElement
|
|
17
|
-
? Array.from(supportFilterElement.
|
|
15
|
+
? Array.from(supportFilterElement.querySelectorAll('input[type="checkbox"]:checked')).map(checkbox => checkbox.value)
|
|
16
|
+
: [];
|
|
17
|
+
// Check for cloud support filter (optional)
|
|
18
|
+
const cloudSupportFilterElement = document.querySelector('#cloudSupportFilterMenu');
|
|
19
|
+
const cloudSupportFilter = cloudSupportFilterElement
|
|
20
|
+
? Array.from(cloudSupportFilterElement.querySelectorAll('input[type="checkbox"]:checked')).map(checkbox => checkbox.value)
|
|
21
|
+
: [];
|
|
22
|
+
// Check for enterprise license filter (optional)
|
|
23
|
+
const enterpriseFilterElement = document.querySelector('#enterpriseFilterMenu');
|
|
24
|
+
const enterpriseFilter = enterpriseFilterElement
|
|
25
|
+
? Array.from(enterpriseFilterElement.querySelectorAll('input[type="checkbox"]:checked')).map(checkbox => checkbox.value)
|
|
18
26
|
: [];
|
|
19
|
-
|
|
20
27
|
const params = getQueryParams();
|
|
21
28
|
const enterpriseSupportFilter = params.support === 'enterprise'; // Check if 'support=enterprise' is in the URL
|
|
22
|
-
const
|
|
23
|
-
|
|
29
|
+
const cloudSupportFilterFromUrl = params.support === 'cloud'; // Check if 'support=cloud' is in the URL
|
|
24
30
|
const table = document.getElementById('componentTable');
|
|
31
|
+
if (!table) return; // Exit early if table doesn't exist
|
|
25
32
|
const trs = table.getElementsByTagName('tr');
|
|
26
|
-
|
|
33
|
+
if (!trs || trs.length === 0) return; // Exit early if no rows found
|
|
27
34
|
for (let i = 1; i < trs.length; i++) {
|
|
28
35
|
const row = trs[i];
|
|
29
36
|
const nameTd = row.querySelector('td[id^="componentName-"]');
|
|
@@ -31,32 +38,96 @@ module.exports.register = function (registry, context) {
|
|
|
31
38
|
const supportTd = row.querySelector('td[id^="componentSupport-"]'); // Support column, if present
|
|
32
39
|
const enterpriseSupportTd = row.querySelector('td[id^="componentLicense-"]'); // Enterprise License column, if present
|
|
33
40
|
const cloudSupportTd = row.querySelector('td[id^="componentCloud-"]'); // Cloud support column, if present
|
|
34
|
-
|
|
35
41
|
if (typeTd) { // Ensure that at least the Type column is present
|
|
36
42
|
const nameText = nameTd ? nameTd.textContent.trim().toLowerCase() : '';
|
|
37
43
|
const typeText = typeTd.textContent.trim().toLowerCase().split(', ').map(item => item.trim());
|
|
38
44
|
const supportText = supportTd ? supportTd.textContent.trim().toLowerCase() : '';
|
|
39
45
|
const enterpriseSupportText = enterpriseSupportTd ? enterpriseSupportTd.textContent.trim().toLowerCase() : ''; // Yes or No
|
|
40
46
|
const cloudSupportText = cloudSupportTd ? cloudSupportTd.textContent.trim().toLowerCase() : ''; // Yes or No
|
|
41
|
-
|
|
47
|
+
// Check cloud support filter
|
|
48
|
+
let cloudSupportMatch = true;
|
|
49
|
+
if (cloudSupportFilter.length > 0 && !cloudSupportFilter.includes('')) {
|
|
50
|
+
// If specific options are selected (not "All")
|
|
51
|
+
cloudSupportMatch = cloudSupportFilter.some(value => {
|
|
52
|
+
if (value === 'yes') return cloudSupportText === 'yes' || cloudSupportText.includes('yes');
|
|
53
|
+
if (value === 'no') return cloudSupportText === 'no' || !cloudSupportText.includes('yes');
|
|
54
|
+
return true;
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
// Check enterprise license filter
|
|
58
|
+
let enterpriseLicenseMatch = true;
|
|
59
|
+
if (enterpriseFilter.length > 0 && !enterpriseFilter.includes('')) {
|
|
60
|
+
// If specific options are selected (not "All")
|
|
61
|
+
enterpriseLicenseMatch = enterpriseFilter.some(value => {
|
|
62
|
+
if (value === 'yes') return enterpriseSupportText === 'yes' || enterpriseSupportText.includes('yes');
|
|
63
|
+
if (value === 'no') return enterpriseSupportText === 'no' || !enterpriseSupportText.includes('yes');
|
|
64
|
+
return true;
|
|
65
|
+
});
|
|
66
|
+
}
|
|
42
67
|
// Determine if the row should be shown
|
|
43
68
|
const showRow =
|
|
44
69
|
((!nameInput || nameText.includes(nameInput)) && // Filter by name if present
|
|
45
70
|
(typeFilter.length === 0 || typeFilter.some(value => typeText.includes(value))) && // Filter by type
|
|
46
71
|
(!supportTd || supportFilter.length === 0 || supportFilter.some(value => supportText.includes(value))) && // Filter by support if present
|
|
47
|
-
(!enterpriseSupportFilter || !enterpriseSupportTd || supportText.includes('enterprise') || enterpriseSupportText === 'yes') // Filter by enterprise support if 'support=enterprise' is in the URL
|
|
48
|
-
&&
|
|
49
|
-
|
|
50
|
-
|
|
72
|
+
(!enterpriseSupportFilter || !enterpriseSupportTd || supportText.includes('enterprise') || enterpriseSupportText === 'yes') && // Filter by enterprise support if 'support=enterprise' is in the URL
|
|
73
|
+
(!cloudSupportFilterFromUrl || !cloudSupportTd || supportText.includes('cloud') || cloudSupportText === 'yes') && // Filter by cloud support if 'support=cloud' is in the URL
|
|
74
|
+
cloudSupportMatch && // Filter by cloud support dropdown
|
|
75
|
+
enterpriseLicenseMatch // Filter by enterprise license dropdown
|
|
51
76
|
);
|
|
52
|
-
|
|
53
77
|
row.style.display = showRow ? '' : 'none';
|
|
54
78
|
} else {
|
|
55
79
|
row.style.display = 'none'; // Hide row if the Type column is missing
|
|
56
80
|
}
|
|
57
81
|
}
|
|
82
|
+
// Update dropdown text based on selections
|
|
83
|
+
updateDropdownText('typeFilter', 'All Types Selected', 'Types Selected');
|
|
84
|
+
const supportMenu = document.getElementById('supportFilterMenu');
|
|
85
|
+
if (supportMenu) {
|
|
86
|
+
updateDropdownText('supportFilter', 'All Support Levels Selected', 'Support Levels Selected');
|
|
87
|
+
}
|
|
88
|
+
const cloudSupportMenu = document.getElementById('cloudSupportFilterMenu');
|
|
89
|
+
if (cloudSupportMenu) {
|
|
90
|
+
updateDropdownText('cloudSupportFilter', 'All Options Selected', 'Options Selected');
|
|
91
|
+
}
|
|
92
|
+
const enterpriseMenu = document.getElementById('enterpriseFilterMenu');
|
|
93
|
+
if (enterpriseMenu) {
|
|
94
|
+
updateDropdownText('enterpriseFilter', 'All Options Selected', 'Options Selected');
|
|
95
|
+
}
|
|
96
|
+
// Update URL parameters based on current filter selections
|
|
97
|
+
updateURLParameters();
|
|
98
|
+
}
|
|
99
|
+
function updateURLParameters() {
|
|
100
|
+
const params = new URLSearchParams();
|
|
101
|
+
// Get current filter values
|
|
102
|
+
const nameInputElement = document.getElementById('componentTableSearch');
|
|
103
|
+
const nameInput = nameInputElement ? nameInputElement.value.trim() : '';
|
|
104
|
+
const typeFilter = Array.from(document.querySelectorAll('#typeFilterMenu input[type="checkbox"]:checked')).map(checkbox => checkbox.value);
|
|
105
|
+
const supportFilterElement = document.querySelector('#supportFilterMenu');
|
|
106
|
+
const supportFilter = supportFilterElement
|
|
107
|
+
? Array.from(supportFilterElement.querySelectorAll('input[type="checkbox"]:checked')).map(checkbox => checkbox.value)
|
|
108
|
+
: [];
|
|
109
|
+
const cloudSupportFilterElement = document.querySelector('#cloudSupportFilterMenu');
|
|
110
|
+
const cloudSupportFilter = cloudSupportFilterElement
|
|
111
|
+
? Array.from(cloudSupportFilterElement.querySelectorAll('input[type="checkbox"]:checked')).map(checkbox => checkbox.value)
|
|
112
|
+
: [];
|
|
113
|
+
const enterpriseFilterElement = document.querySelector('#enterpriseFilterMenu');
|
|
114
|
+
const enterpriseFilter = enterpriseFilterElement
|
|
115
|
+
? Array.from(enterpriseFilterElement.querySelectorAll('input[type="checkbox"]:checked')).map(checkbox => checkbox.value)
|
|
116
|
+
: [];
|
|
117
|
+
// Add parameters to URL if they have values
|
|
118
|
+
if (nameInput) params.set('search', nameInput);
|
|
119
|
+
if (typeFilter.length > 0) params.set('type', typeFilter.join(','));
|
|
120
|
+
if (supportFilter.length > 0) params.set('support', supportFilter.join(','));
|
|
121
|
+
if (cloudSupportFilter.length > 0 && !cloudSupportFilter.includes('')) {
|
|
122
|
+
params.set('cloud', cloudSupportFilter.join(','));
|
|
123
|
+
}
|
|
124
|
+
if (enterpriseFilter.length > 0 && !enterpriseFilter.includes('')) {
|
|
125
|
+
params.set('enterprise', enterpriseFilter.join(','));
|
|
126
|
+
}
|
|
127
|
+
// Update the URL without refreshing the page
|
|
128
|
+
const newURL = window.location.pathname + (params.toString() ? '?' + params.toString() : '');
|
|
129
|
+
window.history.replaceState({}, '', newURL);
|
|
58
130
|
}
|
|
59
|
-
|
|
60
131
|
/**
|
|
61
132
|
* Gets the first URL (either Redpanda Connect or Redpanda Cloud) for a given connector from the typesArray.
|
|
62
133
|
* If the cloud option is enabled (`isCloud = true`), it prefers the Redpanda Cloud URL; otherwise, it returns the Redpanda Connect URL.
|
|
@@ -76,12 +147,10 @@ module.exports.register = function (registry, context) {
|
|
|
76
147
|
if (isCloud && redpandaCloudUrl) {
|
|
77
148
|
return redpandaCloudUrl;
|
|
78
149
|
}
|
|
79
|
-
|
|
80
150
|
// Return Connect URL if isCloud is false or no Cloud URL exists
|
|
81
151
|
if (!isCloud && redpandaConnectUrl) {
|
|
82
152
|
return redpandaConnectUrl;
|
|
83
153
|
}
|
|
84
|
-
|
|
85
154
|
// If Cloud URL exists but isCloud is false, fallback to Cloud URL if no Connect URL exists
|
|
86
155
|
if (!isCloud && redpandaCloudUrl) {
|
|
87
156
|
return redpandaCloudUrl;
|
|
@@ -90,7 +159,6 @@ module.exports.register = function (registry, context) {
|
|
|
90
159
|
}
|
|
91
160
|
return ''; // Return an empty string if no URL is found
|
|
92
161
|
}
|
|
93
|
-
|
|
94
162
|
const capitalize = s => s && s[0].toUpperCase() + s.slice(1);
|
|
95
163
|
|
|
96
164
|
/**
|
|
@@ -285,7 +353,6 @@ module.exports.register = function (registry, context) {
|
|
|
285
353
|
function generateConnectorsHTMLTable(connectors, sqlDrivers, isCloud, showAllInfo) {
|
|
286
354
|
return Object.entries(connectors)
|
|
287
355
|
.filter(([_, details]) => {
|
|
288
|
-
|
|
289
356
|
// If isCloud is true, filter out rows that do not support cloud
|
|
290
357
|
return !isCloud || details.isCloudConnectorSupported;
|
|
291
358
|
})
|
|
@@ -518,41 +585,94 @@ module.exports.register = function (registry, context) {
|
|
|
518
585
|
if (row.support_level) uniqueSupportLevel.add(row.support_level);
|
|
519
586
|
});
|
|
520
587
|
|
|
521
|
-
const
|
|
588
|
+
const createDropdownCheckboxOptions = (values, id) =>
|
|
522
589
|
Array.from(values)
|
|
523
|
-
.map(value =>
|
|
590
|
+
.map(value => `
|
|
591
|
+
<label class="dropdown-checkbox-option">
|
|
592
|
+
<input type="checkbox" value="${value}" checked onchange="filterComponentTable()">
|
|
593
|
+
<span>${capitalize(value).replace("_", " ")}</span>
|
|
594
|
+
</label>`)
|
|
524
595
|
.join('');
|
|
525
596
|
|
|
526
597
|
let tableHtml = `
|
|
527
598
|
<div class="table-filters">
|
|
528
599
|
<input class="table-search" type="text" id="componentTableSearch" onkeyup="filterComponentTable()" placeholder="Search for components...">
|
|
529
|
-
<
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
600
|
+
<div class="filter-group">
|
|
601
|
+
<label for="typeFilterToggle">Type:</label>
|
|
602
|
+
<div class="dropdown-checkbox-wrapper">
|
|
603
|
+
<button type="button" class="dropdown-checkbox-toggle" id="typeFilterToggle" onclick="toggleDropdownCheckbox('typeFilter')" aria-expanded="false" aria-haspopup="true" aria-controls="typeFilterMenu">
|
|
604
|
+
<span class="dropdown-text">All Types Selected</span>
|
|
605
|
+
<span class="dropdown-arrow">βΌ</span>
|
|
606
|
+
</button>
|
|
607
|
+
<div class="dropdown-checkbox-menu" id="typeFilterMenu" role="menu" aria-labelledby="typeFilterToggle">
|
|
608
|
+
${createDropdownCheckboxOptions(types, 'typeFilter')}
|
|
609
|
+
</div>
|
|
610
|
+
</div>
|
|
611
|
+
</div>
|
|
533
612
|
`;
|
|
534
613
|
|
|
535
614
|
if (!isCloud) {
|
|
536
615
|
tableHtml += `
|
|
537
|
-
<
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
616
|
+
<div class="filter-group">
|
|
617
|
+
<label for="supportFilterToggle" id="labelForSupportFilter">Support:</label>
|
|
618
|
+
<div class="dropdown-checkbox-wrapper">
|
|
619
|
+
<button type="button" class="dropdown-checkbox-toggle" id="supportFilterToggle" onclick="toggleDropdownCheckbox('supportFilter')" aria-expanded="false" aria-haspopup="true" aria-controls="supportFilterMenu">
|
|
620
|
+
<span class="dropdown-text">All Support Levels Selected</span>
|
|
621
|
+
<span class="dropdown-arrow">βΌ</span>
|
|
622
|
+
</button>
|
|
623
|
+
<div class="dropdown-checkbox-menu" id="supportFilterMenu" role="menu" aria-labelledby="supportFilterToggle">
|
|
624
|
+
${createDropdownCheckboxOptions(uniqueSupportLevel, 'supportFilter')}
|
|
625
|
+
</div>
|
|
626
|
+
</div>
|
|
627
|
+
</div>
|
|
541
628
|
`;
|
|
542
629
|
}
|
|
543
630
|
|
|
544
631
|
if (showAllInfo) {
|
|
545
632
|
tableHtml += `
|
|
546
|
-
<
|
|
547
|
-
|
|
548
|
-
<
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
633
|
+
<div class="filter-group">
|
|
634
|
+
<label for="cloudSupportFilterToggle">Available in Cloud:</label>
|
|
635
|
+
<div class="dropdown-checkbox-wrapper">
|
|
636
|
+
<button type="button" class="dropdown-checkbox-toggle" id="cloudSupportFilterToggle" onclick="toggleDropdownCheckbox('cloudSupportFilter')" aria-expanded="false" aria-haspopup="true" aria-controls="cloudSupportFilterMenu">
|
|
637
|
+
<span class="dropdown-text">All Options Selected</span>
|
|
638
|
+
<span class="dropdown-arrow">βΌ</span>
|
|
639
|
+
</button>
|
|
640
|
+
<div class="dropdown-checkbox-menu" id="cloudSupportFilterMenu" role="menu" aria-labelledby="cloudSupportFilterToggle">
|
|
641
|
+
<label class="dropdown-checkbox-option">
|
|
642
|
+
<input type="checkbox" value="yes" checked onchange="filterComponentTable()">
|
|
643
|
+
<span>Yes</span>
|
|
644
|
+
</label>
|
|
645
|
+
<label class="dropdown-checkbox-option">
|
|
646
|
+
<input type="checkbox" value="no" checked onchange="filterComponentTable()">
|
|
647
|
+
<span>No</span>
|
|
648
|
+
</label>
|
|
649
|
+
</div>
|
|
650
|
+
</div>
|
|
651
|
+
</div>
|
|
652
|
+
<div class="filter-group">
|
|
653
|
+
<label for="enterpriseFilterToggle">Enterprise License:</label>
|
|
654
|
+
<div class="dropdown-checkbox-wrapper">
|
|
655
|
+
<button type="button" class="dropdown-checkbox-toggle" id="enterpriseFilterToggle" onclick="toggleDropdownCheckbox('enterpriseFilter')" aria-expanded="false" aria-haspopup="true" aria-controls="enterpriseFilterMenu">
|
|
656
|
+
<span class="dropdown-text">All Options Selected</span>
|
|
657
|
+
<span class="dropdown-arrow">βΌ</span>
|
|
658
|
+
</button>
|
|
659
|
+
<div class="dropdown-checkbox-menu" id="enterpriseFilterMenu" role="menu" aria-labelledby="enterpriseFilterToggle">
|
|
660
|
+
<label class="dropdown-checkbox-option">
|
|
661
|
+
<input type="checkbox" value="yes" checked onchange="filterComponentTable()">
|
|
662
|
+
<span>Yes</span>
|
|
663
|
+
</label>
|
|
664
|
+
<label class="dropdown-checkbox-option">
|
|
665
|
+
<input type="checkbox" value="no" checked onchange="filterComponentTable()">
|
|
666
|
+
<span>No</span>
|
|
667
|
+
</label>
|
|
668
|
+
</div>
|
|
669
|
+
</div>
|
|
670
|
+
</div>
|
|
552
671
|
`;
|
|
553
672
|
}
|
|
554
673
|
|
|
555
674
|
tableHtml += `</div>
|
|
675
|
+
<!-- CSS styles are defined in the external redpanda-connect-filters.css stylesheet -->
|
|
556
676
|
<table class="tableblock frame-all grid-all stripes-even no-clip stretch component-table sortable" id="componentTable">
|
|
557
677
|
<colgroup>
|
|
558
678
|
${showAllInfo
|
|
@@ -580,6 +700,7 @@ module.exports.register = function (registry, context) {
|
|
|
580
700
|
</table>
|
|
581
701
|
<script>
|
|
582
702
|
${filterComponentTable.toString()}
|
|
703
|
+
${updateURLParameters.toString()}
|
|
583
704
|
function getQueryParams() {
|
|
584
705
|
const params = {};
|
|
585
706
|
const searchParams = new URLSearchParams(window.location.search);
|
|
@@ -589,30 +710,223 @@ module.exports.register = function (registry, context) {
|
|
|
589
710
|
return params;
|
|
590
711
|
}
|
|
591
712
|
|
|
592
|
-
//
|
|
713
|
+
// Define global dropdown functions (shared between macros)
|
|
714
|
+
window.initializeDropdownFunctions = window.initializeDropdownFunctions || function() {
|
|
715
|
+
// Component type dropdown toggle function
|
|
716
|
+
window.toggleComponentTypeDropdown = function() {
|
|
717
|
+
const toggle = document.getElementById('componentTypeDropdownToggle');
|
|
718
|
+
const menu = document.getElementById('componentTypeDropdownMenu');
|
|
719
|
+
|
|
720
|
+
if (!toggle || !menu) return;
|
|
721
|
+
|
|
722
|
+
const isOpen = menu.classList.contains('show');
|
|
723
|
+
|
|
724
|
+
// Close all other dropdowns first (including filter dropdowns)
|
|
725
|
+
document.querySelectorAll('.dropdown-checkbox-menu.show, .dropdown-menu.show').forEach(dropdown => {
|
|
726
|
+
if (dropdown !== menu) {
|
|
727
|
+
dropdown.classList.remove('show');
|
|
728
|
+
const otherToggle = dropdown.parentNode.querySelector('.dropdown-checkbox-toggle, .dropdown-toggle');
|
|
729
|
+
if (otherToggle) {
|
|
730
|
+
otherToggle.classList.remove('open');
|
|
731
|
+
otherToggle.setAttribute('aria-expanded', 'false');
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
});
|
|
735
|
+
|
|
736
|
+
// Toggle current dropdown
|
|
737
|
+
if (isOpen) {
|
|
738
|
+
menu.classList.remove('show');
|
|
739
|
+
toggle.classList.remove('open');
|
|
740
|
+
toggle.setAttribute('aria-expanded', 'false');
|
|
741
|
+
} else {
|
|
742
|
+
menu.classList.add('show');
|
|
743
|
+
toggle.classList.add('open');
|
|
744
|
+
toggle.setAttribute('aria-expanded', 'true');
|
|
745
|
+
// Focus first option
|
|
746
|
+
const firstOption = menu.querySelector('.dropdown-option');
|
|
747
|
+
if (firstOption) firstOption.focus();
|
|
748
|
+
}
|
|
749
|
+
};
|
|
750
|
+
|
|
751
|
+
};
|
|
752
|
+
|
|
753
|
+
// Initialize the functions
|
|
754
|
+
window.initializeDropdownFunctions();
|
|
755
|
+
|
|
756
|
+
function toggleDropdownCheckbox(filterId) {
|
|
757
|
+
const toggle = document.getElementById(filterId + 'Toggle');
|
|
758
|
+
const menu = document.getElementById(filterId + 'Menu');
|
|
759
|
+
|
|
760
|
+
if (!toggle || !menu) return;
|
|
761
|
+
|
|
762
|
+
const isOpen = menu.classList.contains('show');
|
|
763
|
+
|
|
764
|
+
// Close all other dropdowns first
|
|
765
|
+
document.querySelectorAll('.dropdown-checkbox-menu.show').forEach(dropdown => {
|
|
766
|
+
if (dropdown !== menu) {
|
|
767
|
+
dropdown.classList.remove('show');
|
|
768
|
+
const otherToggle = dropdown.parentNode.querySelector('.dropdown-checkbox-toggle');
|
|
769
|
+
if (otherToggle) {
|
|
770
|
+
otherToggle.classList.remove('open');
|
|
771
|
+
otherToggle.setAttribute('aria-expanded', 'false');
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
});
|
|
775
|
+
|
|
776
|
+
// Toggle current dropdown
|
|
777
|
+
if (isOpen) {
|
|
778
|
+
menu.classList.remove('show');
|
|
779
|
+
toggle.classList.remove('open');
|
|
780
|
+
toggle.setAttribute('aria-expanded', 'false');
|
|
781
|
+
} else {
|
|
782
|
+
menu.classList.add('show');
|
|
783
|
+
toggle.classList.add('open');
|
|
784
|
+
toggle.setAttribute('aria-expanded', 'true');
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
function updateDropdownText(filterId, allSelectedText, someSelectedText) {
|
|
789
|
+
const menu = document.getElementById(filterId + 'Menu');
|
|
790
|
+
const toggle = document.getElementById(filterId + 'Toggle');
|
|
791
|
+
|
|
792
|
+
if (!menu || !toggle) return;
|
|
793
|
+
|
|
794
|
+
const checkboxes = menu.querySelectorAll('input[type="checkbox"]');
|
|
795
|
+
const checkedCount = menu.querySelectorAll('input[type="checkbox"]:checked').length;
|
|
796
|
+
const totalCount = checkboxes.length;
|
|
797
|
+
const textElement = toggle.querySelector('.dropdown-text');
|
|
798
|
+
|
|
799
|
+
if (!textElement) return;
|
|
800
|
+
|
|
801
|
+
if (checkedCount === 0) {
|
|
802
|
+
textElement.textContent = 'None Selected';
|
|
803
|
+
} else if (checkedCount === totalCount) {
|
|
804
|
+
textElement.textContent = allSelectedText;
|
|
805
|
+
} else if (checkedCount === 1) {
|
|
806
|
+
const checkedBox = menu.querySelector('input[type="checkbox"]:checked');
|
|
807
|
+
if (checkedBox) {
|
|
808
|
+
const label = checkedBox.nextElementSibling;
|
|
809
|
+
textElement.textContent = label ? label.textContent : getSingularText(someSelectedText);
|
|
810
|
+
}
|
|
811
|
+
} else {
|
|
812
|
+
textElement.textContent = checkedCount + ' ' + someSelectedText;
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
function getSingularText(pluralText) {
|
|
817
|
+
// Handle various plural patterns and convert to singular
|
|
818
|
+
if (pluralText.includes('Types Selected')) {
|
|
819
|
+
return 'Type Selected';
|
|
820
|
+
} else if (pluralText.includes('Support Levels Selected')) {
|
|
821
|
+
return 'Support Level Selected';
|
|
822
|
+
} else if (pluralText.includes('Options Selected')) {
|
|
823
|
+
return 'Option Selected';
|
|
824
|
+
} else if (pluralText.includes('Items Selected')) {
|
|
825
|
+
return 'Item Selected';
|
|
826
|
+
} else if (pluralText.includes('Categories Selected')) {
|
|
827
|
+
return 'Category Selected';
|
|
828
|
+
} else if (pluralText.includes('Filters Selected')) {
|
|
829
|
+
return 'Filter Selected';
|
|
830
|
+
} else if (pluralText.endsWith('s Selected')) {
|
|
831
|
+
// Generic fallback for words ending in 's Selected'
|
|
832
|
+
return pluralText.replace(/s Selected$/, ' Selected');
|
|
833
|
+
} else if (pluralText.endsWith('ies Selected')) {
|
|
834
|
+
// Handle words ending in 'ies' (e.g., "Categories Selected" -> "Category Selected")
|
|
835
|
+
return pluralText.replace(/ies Selected$/, 'y Selected');
|
|
836
|
+
} else {
|
|
837
|
+
// If no pattern matches, return as-is
|
|
838
|
+
return pluralText;
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
// Close dropdown when clicking outside (local handler for filter dropdowns only)
|
|
843
|
+
document.addEventListener('click', function(event) {
|
|
844
|
+
if (!event.target.closest('.dropdown-checkbox-wrapper')) {
|
|
845
|
+
document.querySelectorAll('.dropdown-checkbox-menu.show').forEach(menu => {
|
|
846
|
+
menu.classList.remove('show');
|
|
847
|
+
const toggle = menu.parentNode.querySelector('.dropdown-checkbox-toggle');
|
|
848
|
+
if (toggle) {
|
|
849
|
+
toggle.classList.remove('open');
|
|
850
|
+
toggle.setAttribute('aria-expanded', 'false');
|
|
851
|
+
}
|
|
852
|
+
});
|
|
853
|
+
}
|
|
854
|
+
});
|
|
855
|
+
|
|
856
|
+
// Add keyboard navigation support (local handler for filter dropdowns only)
|
|
857
|
+
document.addEventListener('keydown', function(event) {
|
|
858
|
+
if (event.key === 'Escape') {
|
|
859
|
+
// Close all open filter dropdowns on Escape
|
|
860
|
+
document.querySelectorAll('.dropdown-checkbox-menu.show').forEach(menu => {
|
|
861
|
+
menu.classList.remove('show');
|
|
862
|
+
const toggle = menu.parentNode.querySelector('.dropdown-checkbox-toggle');
|
|
863
|
+
if (toggle) {
|
|
864
|
+
toggle.classList.remove('open');
|
|
865
|
+
toggle.setAttribute('aria-expanded', 'false');
|
|
866
|
+
toggle.focus(); // Return focus to toggle button
|
|
867
|
+
}
|
|
868
|
+
});
|
|
869
|
+
}
|
|
870
|
+
});
|
|
871
|
+
|
|
872
|
+
// Initialize filters from URL parameters
|
|
593
873
|
document.addEventListener('DOMContentLoaded', function() {
|
|
594
874
|
const params = getQueryParams();
|
|
595
875
|
const search = document.getElementById('componentTableSearch');
|
|
596
|
-
const
|
|
597
|
-
const
|
|
876
|
+
const typeFilterMenu = document.getElementById('typeFilterMenu');
|
|
877
|
+
const supportFilterMenu = document.getElementById('supportFilterMenu');
|
|
878
|
+
const cloudSupportFilterMenu = document.getElementById('cloudSupportFilterMenu');
|
|
879
|
+
const enterpriseFilterMenu = document.getElementById('enterpriseFilterMenu');
|
|
880
|
+
|
|
598
881
|
if (params.search && search) {
|
|
599
882
|
search.value = params.search;
|
|
600
883
|
}
|
|
601
|
-
|
|
602
|
-
|
|
884
|
+
|
|
885
|
+
if (params.type && typeFilterMenu) {
|
|
886
|
+
const types = params.type.split(',');
|
|
887
|
+
// First uncheck all checkboxes
|
|
888
|
+
typeFilterMenu.querySelectorAll('input[type="checkbox"]').forEach(cb => cb.checked = false);
|
|
889
|
+
// Then check only the ones in the URL
|
|
890
|
+
types.forEach(type => {
|
|
891
|
+
const checkbox = typeFilterMenu.querySelector(\`input[value="\${type}"]\`);
|
|
892
|
+
if (checkbox) checkbox.checked = true;
|
|
893
|
+
});
|
|
603
894
|
}
|
|
604
|
-
|
|
605
|
-
|
|
895
|
+
|
|
896
|
+
if (params.support && supportFilterMenu) {
|
|
897
|
+
const supports = params.support.split(',');
|
|
898
|
+
// First uncheck all checkboxes
|
|
899
|
+
supportFilterMenu.querySelectorAll('input[type="checkbox"]').forEach(cb => cb.checked = false);
|
|
900
|
+
// Then check only the ones in the URL
|
|
901
|
+
supports.forEach(support => {
|
|
902
|
+
const checkbox = supportFilterMenu.querySelector(\`input[value="\${support}"]\`);
|
|
903
|
+
if (checkbox) checkbox.checked = true;
|
|
904
|
+
});
|
|
606
905
|
}
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
906
|
+
|
|
907
|
+
if (params.cloud && cloudSupportFilterMenu) {
|
|
908
|
+
const cloudOptions = params.cloud.split(',');
|
|
909
|
+
// First uncheck all checkboxes
|
|
910
|
+
cloudSupportFilterMenu.querySelectorAll('input[type="checkbox"]').forEach(cb => cb.checked = false);
|
|
911
|
+
// Then check only the ones in the URL
|
|
912
|
+
cloudOptions.forEach(option => {
|
|
913
|
+
const checkbox = cloudSupportFilterMenu.querySelector(\`input[value="\${option}"]\`);
|
|
914
|
+
if (checkbox) checkbox.checked = true;
|
|
614
915
|
});
|
|
615
|
-
}
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
if (params.enterprise && enterpriseFilterMenu) {
|
|
919
|
+
const enterpriseOptions = params.enterprise.split(',');
|
|
920
|
+
// First uncheck all checkboxes
|
|
921
|
+
enterpriseFilterMenu.querySelectorAll('input[type="checkbox"]').forEach(cb => cb.checked = false);
|
|
922
|
+
// Then check only the ones in the URL
|
|
923
|
+
enterpriseOptions.forEach(option => {
|
|
924
|
+
const checkbox = enterpriseFilterMenu.querySelector(\`input[value="\${option}"]\`);
|
|
925
|
+
if (checkbox) checkbox.checked = true;
|
|
926
|
+
});
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
filterComponentTable();
|
|
616
930
|
});
|
|
617
931
|
</script>
|
|
618
932
|
`;
|
|
@@ -737,35 +1051,71 @@ module.exports.register = function (registry, context) {
|
|
|
737
1051
|
// Build the dropdown for types with links depending on the current component
|
|
738
1052
|
let typeDropdown = '';
|
|
739
1053
|
if (sortedTypes.length > 1) {
|
|
740
|
-
const
|
|
1054
|
+
const dropdownOptions = sortedTypes.map(typeObj => {
|
|
741
1055
|
const link = (component === 'Cloud' && typeObj.redpandaCloudUrl) || typeObj.redpandaConnectUrl;
|
|
742
|
-
return `<
|
|
1056
|
+
return `<a href="${link}" class="dropdown-option" role="menuitem" tabindex="-1">${capitalize(typeObj.type)}</a>`;
|
|
743
1057
|
}).join('');
|
|
744
1058
|
typeDropdown = `
|
|
745
|
-
<
|
|
746
|
-
<
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
});
|
|
757
|
-
});
|
|
758
|
-
</script>`;
|
|
1059
|
+
<div class="dropdown-wrapper">
|
|
1060
|
+
<p class="type-dropdown-container"><strong>Type:</strong>
|
|
1061
|
+
<button type="button" class="dropdown-toggle" id="componentTypeDropdownToggle" onclick="toggleComponentTypeDropdown()" aria-expanded="false" aria-haspopup="true" aria-controls="componentTypeDropdownMenu">
|
|
1062
|
+
<span class="dropdown-text">${capitalize(sortedTypes[0].type)}</span>
|
|
1063
|
+
<span class="dropdown-arrow">βΌ</span>
|
|
1064
|
+
</button>
|
|
1065
|
+
<div class="dropdown-menu" id="componentTypeDropdownMenu" role="menu" aria-labelledby="componentTypeDropdownToggle">
|
|
1066
|
+
${dropdownOptions}
|
|
1067
|
+
</div>
|
|
1068
|
+
</p>
|
|
1069
|
+
</div>`;
|
|
759
1070
|
}
|
|
760
1071
|
// Return the metadata block with consistent layout
|
|
761
1072
|
return self.createBlock(parent, 'pass', `
|
|
762
1073
|
<div class="metadata-block">
|
|
763
|
-
<div
|
|
1074
|
+
<div class="metadata-content">
|
|
764
1075
|
${typeDropdown}
|
|
765
1076
|
${availableInInfo}
|
|
766
1077
|
${enterpriseLicenseInfo}
|
|
767
1078
|
</div>
|
|
768
|
-
</div
|
|
1079
|
+
</div>
|
|
1080
|
+
<script>
|
|
1081
|
+
// Define global dropdown functions directly (shared between macros)
|
|
1082
|
+
if (!window.toggleComponentTypeDropdown) {
|
|
1083
|
+
window.toggleComponentTypeDropdown = function() {
|
|
1084
|
+
const toggle = document.getElementById('componentTypeDropdownToggle');
|
|
1085
|
+
const menu = document.getElementById('componentTypeDropdownMenu');
|
|
1086
|
+
|
|
1087
|
+
if (!toggle || !menu) return;
|
|
1088
|
+
|
|
1089
|
+
const isOpen = menu.classList.contains('show');
|
|
1090
|
+
|
|
1091
|
+
// Close all other dropdowns first (including filter dropdowns)
|
|
1092
|
+
document.querySelectorAll('.dropdown-checkbox-menu.show, .dropdown-menu.show').forEach(dropdown => {
|
|
1093
|
+
if (dropdown !== menu) {
|
|
1094
|
+
dropdown.classList.remove('show');
|
|
1095
|
+
const otherToggle = dropdown.parentNode.querySelector('.dropdown-checkbox-toggle, .dropdown-toggle');
|
|
1096
|
+
if (otherToggle) {
|
|
1097
|
+
otherToggle.classList.remove('open');
|
|
1098
|
+
otherToggle.setAttribute('aria-expanded', 'false');
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
});
|
|
1102
|
+
|
|
1103
|
+
// Toggle current dropdown
|
|
1104
|
+
if (isOpen) {
|
|
1105
|
+
menu.classList.remove('show');
|
|
1106
|
+
toggle.classList.remove('open');
|
|
1107
|
+
toggle.setAttribute('aria-expanded', 'false');
|
|
1108
|
+
} else {
|
|
1109
|
+
menu.classList.add('show');
|
|
1110
|
+
toggle.classList.add('open');
|
|
1111
|
+
toggle.setAttribute('aria-expanded', 'true');
|
|
1112
|
+
// Focus first option
|
|
1113
|
+
const firstOption = menu.querySelector('.dropdown-option');
|
|
1114
|
+
if (firstOption) firstOption.focus();
|
|
1115
|
+
}
|
|
1116
|
+
};
|
|
1117
|
+
}
|
|
1118
|
+
</script>`);
|
|
769
1119
|
});
|
|
770
1120
|
});
|
|
771
1121
|
|
package/package.json
CHANGED
|
@@ -90,6 +90,8 @@ generate-docs:
|
|
|
90
90
|
@mkdir -p "$(OUTPUT_DIR)"
|
|
91
91
|
@cd $(TOOL_ROOT) && \
|
|
92
92
|
$(PYTHON) json-to-asciidoc/generate_docs.py --output-dir "$(OUTPUT_DIR)"
|
|
93
|
+
@echo "π Copying properties-output.json to $(OUTPUT_DIR)β¦"
|
|
94
|
+
@cp "$(TOOL_ROOT)/gen/properties-output.json" "$(OUTPUT_DIR)/"
|
|
93
95
|
@echo "β
Docs generated at $(OUTPUT_DIR)"
|
|
94
96
|
|
|
95
97
|
# --- Debug helper to print all the key paths/vars ---
|
|
@@ -261,6 +261,12 @@ def generate_property_doc(key, value):
|
|
|
261
261
|
if prop_type in ["string", "array", "number", "boolean", "integer"]:
|
|
262
262
|
lines.append(f"*Type:* {prop_type}\n\n")
|
|
263
263
|
|
|
264
|
+
# Add aliases if they exist
|
|
265
|
+
aliases = value.get("aliases")
|
|
266
|
+
if aliases and len(aliases) > 0:
|
|
267
|
+
aliases_str = ", ".join(f"`{alias}`" for alias in aliases)
|
|
268
|
+
lines.append(f"*Aliases:* {aliases_str}\n\n")
|
|
269
|
+
|
|
264
270
|
if value.get("maximum") is not None and value.get("minimum") is not None:
|
|
265
271
|
lines.append(
|
|
266
272
|
f"*Accepted values:* [`{value.get('minimum')}`, `{value.get('maximum')}`]\n\n"
|
|
@@ -135,14 +135,67 @@ function mergeOverrides(target, overrides) {
|
|
|
135
135
|
return target;
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
+
/**
|
|
139
|
+
* Resolves $ref references in an object by replacing them with their definitions.
|
|
140
|
+
* Supports JSON Pointer style references like "#/definitions/client_certs".
|
|
141
|
+
*
|
|
142
|
+
* @param {Object} obj - The object to resolve references in
|
|
143
|
+
* @param {Object} root - The root object containing definitions
|
|
144
|
+
* @returns {Object} The object with references resolved
|
|
145
|
+
*/
|
|
146
|
+
function resolveReferences(obj, root) {
|
|
147
|
+
if (!obj || typeof obj !== 'object') {
|
|
148
|
+
return obj;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (Array.isArray(obj)) {
|
|
152
|
+
return obj.map(item => resolveReferences(item, root));
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const result = {};
|
|
156
|
+
|
|
157
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
158
|
+
if (key === '$ref' && typeof value === 'string') {
|
|
159
|
+
// Handle JSON Pointer style references
|
|
160
|
+
if (value.startsWith('#/')) {
|
|
161
|
+
const path = value.substring(2).split('/');
|
|
162
|
+
let resolved = root;
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
for (const segment of path) {
|
|
166
|
+
resolved = resolved[segment];
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (resolved === undefined) {
|
|
170
|
+
throw new Error(`Reference path not found: ${value}`);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Merge the resolved object, but don't process $ref in the resolved object
|
|
174
|
+
// to avoid infinite recursion
|
|
175
|
+
Object.assign(result, resolved);
|
|
176
|
+
} catch (err) {
|
|
177
|
+
throw new Error(`Failed to resolve reference "${value}": ${err.message}`);
|
|
178
|
+
}
|
|
179
|
+
} else {
|
|
180
|
+
throw new Error(`Unsupported reference format: ${value}. Only JSON Pointer references starting with '#/' are supported.`);
|
|
181
|
+
}
|
|
182
|
+
} else {
|
|
183
|
+
// Recursively resolve references in nested objects
|
|
184
|
+
result[key] = resolveReferences(value, root);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return result;
|
|
189
|
+
}
|
|
190
|
+
|
|
138
191
|
/**
|
|
139
192
|
* Generates documentation files for RPCN connectors using Handlebars templates.
|
|
140
193
|
*
|
|
141
|
-
* Depending on the {@link writeFullDrafts} flag, generates either partial documentation files for connector fields and examples, or full draft documentation for each connector component. Supports merging override data and skips draft generation for components marked as deprecated.
|
|
194
|
+
* Depending on the {@link writeFullDrafts} flag, generates either partial documentation files for connector fields and examples, or full draft documentation for each connector component. Supports merging override data with $ref references and skips draft generation for components marked as deprecated.
|
|
142
195
|
*
|
|
143
196
|
* @param {Object} options - Configuration options for documentation generation.
|
|
144
197
|
* @param {string} options.data - Path to the connector data file (JSON or YAML).
|
|
145
|
-
* @param {string} [options.overrides] - Optional path to a JSON file with override data.
|
|
198
|
+
* @param {string} [options.overrides] - Optional path to a JSON file with override data. Supports $ref references in JSON Pointer format (e.g., "#/definitions/client_certs").
|
|
146
199
|
* @param {string} options.template - Path to the main Handlebars template.
|
|
147
200
|
* @param {string} [options.templateIntro] - Path to the intro partial template (used in full draft mode).
|
|
148
201
|
* @param {string} [options.templateFields] - Path to the fields partial template.
|
|
@@ -150,7 +203,7 @@ function mergeOverrides(target, overrides) {
|
|
|
150
203
|
* @param {boolean} options.writeFullDrafts - If true, generates full draft documentation; otherwise, generates partials.
|
|
151
204
|
* @returns {Promise<Object>} An object summarizing the number and paths of generated partials and drafts.
|
|
152
205
|
*
|
|
153
|
-
* @throws {Error} If reading or parsing input files fails,
|
|
206
|
+
* @throws {Error} If reading or parsing input files fails, if template rendering fails for a component, or if $ref references cannot be resolved.
|
|
154
207
|
*
|
|
155
208
|
* @remark
|
|
156
209
|
* When generating full drafts, components with a `status` of `'deprecated'` are skipped.
|
|
@@ -175,7 +228,11 @@ async function generateRpcnConnectorDocs(options) {
|
|
|
175
228
|
if (overrides) {
|
|
176
229
|
const ovRaw = fs.readFileSync(overrides, 'utf8');
|
|
177
230
|
const ovObj = JSON.parse(ovRaw);
|
|
178
|
-
|
|
231
|
+
|
|
232
|
+
// Resolve any $ref references in the overrides
|
|
233
|
+
const resolvedOverrides = resolveReferences(ovObj, ovObj);
|
|
234
|
+
|
|
235
|
+
mergeOverrides(dataObj, resolvedOverrides);
|
|
179
236
|
}
|
|
180
237
|
|
|
181
238
|
// Compile the βmainβ template (used when writeFullDrafts = true)
|
|
@@ -285,5 +342,6 @@ async function generateRpcnConnectorDocs(options) {
|
|
|
285
342
|
|
|
286
343
|
module.exports = {
|
|
287
344
|
generateRpcnConnectorDocs,
|
|
288
|
-
mergeOverrides
|
|
345
|
+
mergeOverrides,
|
|
346
|
+
resolveReferences
|
|
289
347
|
};
|