@redpanda-data/docs-extensions-and-macros 3.5.10 → 3.6.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.
package/README.adoc CHANGED
@@ -516,7 +516,7 @@ asciidoc:
516
516
 
517
517
  This macro generates a tabbed interface to display Redpanda Connect components by category.
518
518
 
519
- The categories are fetched from the `connectCategoriesData` that's generated in the << Component category aggregator>> extension.
519
+ The categories are fetched from the `connectCategoriesData` that's generated in the <<Component category aggregator>> extension.
520
520
 
521
521
  ==== Usage
522
522
 
@@ -536,7 +536,7 @@ asciidoc:
536
536
 
537
537
  This macro generates a searchable table of all Redpanda Connect components with filters for support and type.
538
538
 
539
- The types are fetched from the `flatComponentsData` that's generated in the << Component category aggregator>> extension.
539
+ The types are fetched from the `flatComponentsData` that's generated in the <<Component category aggregator>> extension.
540
540
 
541
541
  ==== Usage
542
542
 
@@ -556,7 +556,7 @@ asciidoc:
556
556
 
557
557
  This macro generates a dropdown of other supported types for a particular component, allowing users to switch between different types.
558
558
 
559
- The types are fetched from the `flatComponentsData` that's generated in the << Component category aggregator>> extension.
559
+ The types are fetched from the `flatComponentsData` that's generated in the <<Component category aggregator>> extension.
560
560
 
561
561
  ==== Usage
562
562
 
@@ -0,0 +1,84 @@
1
+ 'use strict'
2
+ const https = require('https');
3
+ const Papa = require('papaparse');
4
+
5
+ const CSV_PATH = 'redpanda_connect.csv'
6
+ const GITHUB_OWNER = 'redpanda-data'
7
+ const GITHUB_REPO = 'rp-connect-docs'
8
+ const GITHUB_REF = 'connect-csv'
9
+ /* const csvUrl = 'https://localhost:3000/csv';
10
+ process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; */
11
+
12
+ module.exports.register = function ({ config,contentCatalog }) {
13
+ const logger = this.getLogger('redpanda-connect-info-extension');
14
+
15
+ async function loadOctokit() {
16
+ const { Octokit } = await import('@octokit/rest');
17
+ if (!process.env.REDPANDA_GITHUB_TOKEN) return new Octokit()
18
+ return new Octokit({
19
+ auth: process.env.REDPANDA_GITHUB_TOKEN,
20
+ });
21
+ }
22
+
23
+ this.once('contentClassified', async ({ siteCatalog, contentCatalog }) => {
24
+ const redpandaConnect = contentCatalog.getComponents().find(component => component.name === 'redpanda-connect')
25
+ const redpandaCloud = contentCatalog.getComponents().find(component => component.name === 'redpanda-cloud')
26
+ if (!redpandaConnect) return
27
+ const pages = contentCatalog.getPages()
28
+ try {
29
+ // Fetch CSV data and parse it
30
+ const csvData = await fetchCSV();
31
+ const parsedData = Papa.parse(csvData, { header: true, skipEmptyLines: true });
32
+ const enrichedData = enrichCsvDataWithUrls(parsedData, pages, logger);
33
+ parsedData.data = enrichedData
34
+ redpandaConnect.latest.asciidoc.attributes.csvData = parsedData;
35
+ redpandaCloud.latest.asciidoc.attributes.csvData = parsedData;
36
+
37
+ } catch (error) {
38
+ logger.error('Error fetching or parsing CSV data:', error.message);
39
+ logger.error(error.stack);
40
+ }
41
+ });
42
+
43
+ async function fetchCSV() {
44
+ const octokit = await loadOctokit();
45
+ try {
46
+ const { data: fileContent } = await octokit.rest.repos.getContent({
47
+ owner: GITHUB_OWNER,
48
+ repo: GITHUB_REPO,
49
+ path: CSV_PATH,
50
+ ref: GITHUB_REF,
51
+ });
52
+ return Buffer.from(fileContent.content, 'base64').toString('utf8');
53
+ } catch (error) {
54
+ console.error('Error fetching Redpanda Connect catalog from GitHub:', error);
55
+ return [];
56
+ }
57
+ }
58
+
59
+ function enrichCsvDataWithUrls(parsedData, connectPages, logger) {
60
+ return parsedData.data.map(row => {
61
+ // Create a new object with trimmed keys and values
62
+ const trimmedRow = Object.fromEntries(
63
+ Object.entries(row).map(([key, value]) => [key.trim(), value.trim()])
64
+ );
65
+ const connector = trimmedRow.connector;
66
+ const type = trimmedRow.type;
67
+ let url = '';
68
+ for (const file of connectPages) {
69
+ const filePath = file.path;
70
+ if (filePath.endsWith(`${connector}.adoc`) && filePath.includes(`pages/${type}s/`)) {
71
+ url = `../${type}s/${connector}`;
72
+ break;
73
+ }
74
+ }
75
+ if (!url) {
76
+ logger.warn(`No matching URL found for connector: ${connector} of type: ${type}`);
77
+ }
78
+ return {
79
+ ...trimmedRow,
80
+ url: url
81
+ };
82
+ });
83
+ }
84
+ }
@@ -1,6 +1,149 @@
1
1
  'use strict';
2
2
 
3
3
  module.exports.register = function (registry, context) {
4
+
5
+ function filterComponentTable() {
6
+ // Retrieve and standardize filter inputs
7
+ const nameInput = document.getElementById('componentTableSearch').value.trim().toLowerCase();
8
+ const typeFilter = Array.from(document.querySelector('#typeFilter').selectedOptions).map(option => option.value);
9
+
10
+ // Check if the supportFilter element exists
11
+ const supportFilterElement = document.querySelector('#supportFilter');
12
+ const supportFilter = supportFilterElement
13
+ ? Array.from(supportFilterElement.selectedOptions).map(option => option.value)
14
+ : [];
15
+
16
+ const table = document.getElementById('componentTable');
17
+ const trs = table.getElementsByTagName('tr');
18
+
19
+ for (let i = 1; i < trs.length; i++) {
20
+ const row = trs[i];
21
+ const nameTd = row.querySelector('td[id^="componentName-"]');
22
+ const typeTd = row.querySelector('td[id^="componentType-"]');
23
+ const supportTd = row.querySelector('td[id^="componentSupport-"]');
24
+ const typeDropdown = typeTd ? typeTd.querySelector('.type-dropdown') : null;
25
+
26
+ if (nameTd && typeTd) {
27
+ const nameText = nameTd.textContent.trim().toLowerCase();
28
+ const typeText = typeTd.textContent.trim().toLowerCase().split(', ').map(item => item.trim());
29
+ const supportText = supportTd ? supportTd.textContent.trim().toLowerCase() : '';
30
+
31
+ // Determine if the row should be shown
32
+ const showRow =
33
+ ((!nameInput || nameText.includes(nameInput)) &&
34
+ (typeFilter.length === 0 || typeFilter.some(value => typeText.includes(value))) &&
35
+ (!supportTd || supportFilter.length === 0 || supportFilter.some(value => supportText.includes(value)))
36
+ );
37
+
38
+ row.style.display = showRow ? '' : 'none';
39
+
40
+ if (showRow && typeFilter.length > 0 && typeDropdown) {
41
+ const matchingOption = Array.from(typeDropdown.options).find(option =>
42
+ typeFilter.includes(option.text.toLowerCase())
43
+ );
44
+ if (matchingOption) {
45
+ typeDropdown.value = matchingOption.value;
46
+ updateComponentUrl(typeDropdown, false);
47
+ }
48
+ }
49
+ } else {
50
+ row.style.display = 'none'; // Hide row if essential cells are missing
51
+ }
52
+ }
53
+ }
54
+
55
+ const capitalize = s => s && s[0].toUpperCase() + s.slice(1);
56
+
57
+ function processConnectors(parsedData) {
58
+ return parsedData.data.reduce((connectors, row) => {
59
+ const { connector, commercial_name, type, support_level, is_cloud_supported, is_licensed, url } = row;
60
+ let isCloudSupported = is_cloud_supported === 'y'
61
+ if (!connectors[connector]) {
62
+ connectors[connector] = {
63
+ types: new Map(),
64
+ supportLevels: new Map(),
65
+ isLicensed: is_licensed === 'y' ? 'Yes' : 'No',
66
+ isCloudConnectorSupported : isCloudSupported,
67
+ urls: new Set()
68
+ };
69
+ }
70
+ connectors[connector].types.set(capitalize(type), { url, isCloudSupported });
71
+ if (url) connectors[connector].urls.add(url);
72
+ if (!connectors[connector].supportLevels.has(support_level)) {
73
+ connectors[connector].supportLevels.set(support_level, new Set());
74
+ }
75
+ connectors[connector].supportLevels.get(support_level).add(commercial_name);
76
+ return connectors;
77
+ }, {});
78
+ }
79
+
80
+
81
+ function generateConnectorsHTMLTable(connectors, isCloud) {
82
+ return Object.entries(connectors).map(([connector, details], id) => {
83
+ const { types, supportLevels, isCloudConnectorSupported, isLicensed, urls } = details;
84
+ const firstUrl = urls.size > 0 ? urls.values().next().value : null;
85
+
86
+ const typesArray = Array.from(types.entries())
87
+ .map(([type, { url, isCloudSupported }]) => {
88
+ if(isCloud){
89
+ if (isCloudSupported) {
90
+ return url ? `<a href="${url}/">${type}</a>` : `<span>${type}</span>`;
91
+ } else {
92
+ return '';
93
+ }
94
+ }
95
+ else{
96
+ return url ? `<a href="${url}/">${type}</a>` : `<span>${type}</span>`;
97
+ }
98
+ })
99
+ .filter(item => item !== '');
100
+
101
+ const typesStr = typesArray.join(', ');
102
+
103
+ const supportLevelStr = Array.from(supportLevels.entries())
104
+ .map(([level, names]) => `<p><b>${capitalize(level)}</b>: ${Array.from(names).join(', ')}</p>`)
105
+ .join('');
106
+
107
+ const connectorNameHtml = firstUrl
108
+ ? `<code><a href="${firstUrl}/">${connector}</a></code>`
109
+ : `<code><span>${connector}</span></code>`;
110
+
111
+ if (isCloud) {
112
+ if (isCloudConnectorSupported) {
113
+ return `
114
+ <tr id="row-${id}">
115
+ <td class="tableblock halign-left valign-top" id="componentName-${id}">
116
+ <p class="tableblock">${connectorNameHtml}</p>
117
+ </td>
118
+ <td class="tableblock halign-left valign-top" id="componentType-${id}">
119
+ <p class="tableblock">${typesStr}</p>
120
+ </td>
121
+ </tr>`;
122
+ } else {
123
+ return '';
124
+ }
125
+ } else {
126
+ return `
127
+ <tr id="row-${id}">
128
+ <td class="tableblock halign-left valign-top" id="componentName-${id}">
129
+ <p class="tableblock">${connectorNameHtml}</p>
130
+ </td>
131
+ <td class="tableblock halign-left valign-top" id="componentType-${id}">
132
+ <p class="tableblock">${typesStr}</p>
133
+ </td>
134
+ <td class="tableblock halign-left valign-top" id="componentSupport-${id}">
135
+ <p class="tableblock">${supportLevelStr.trim()}</p>
136
+ </td>
137
+ <td class="tableblock halign-left valign-top" id="componentLicense-${id}">
138
+ <p class="tableblock">${isLicensed}</p>
139
+ </td>
140
+ </tr>`;
141
+ }
142
+ }).filter(row => row !== '').join(''); // Filter out empty rows
143
+ }
144
+
145
+
146
+
4
147
  let tabsCounter = 1; // Counter for generating unique IDs
5
148
 
6
149
  // Add the category tabs for components
@@ -16,10 +159,10 @@ module.exports.register = function (registry, context) {
16
159
  if (!categories) return
17
160
 
18
161
  let tabsHtml = `
19
- <div id="${currentTabsId}" class="openblock tabs is-sync is-loaded" data-sync-group-id="${type}">
20
- <div class="content">
21
- <div class="ulist tablist">
22
- <ul role="tablist">`;
162
+ <div id="${currentTabsId}" class="openblock tabs is-sync is-loaded" data-sync-group-id="${type}">
163
+ <div class="content">
164
+ <div class="ulist tablist">
165
+ <ul role="tablist">`;
23
166
 
24
167
  categories.forEach((category, index) => {
25
168
  tabsHtml += `
@@ -63,205 +206,106 @@ module.exports.register = function (registry, context) {
63
206
  const self = this;
64
207
  self.named('component_table');
65
208
  self.process((parent, target, attrs) => {
66
- const flatComponentsData = context.config?.attributes?.flatComponentsData || [];
67
- const driverNameMap = context.config?.attributes?.drivers || [];
68
- const cacheNameMap = context.config?.attributes?.caches || [];
69
- const driverSupportData = context.config?.attributes?.driverSupportData || {};
70
- const cacheSupportData = context.config?.attributes?.cacheSupportData || {};
209
+ const isCloud = attrs["is_cloud"];
210
+ const csvData = context.config?.attributes?.csvData || [];
211
+ const types = new Set();
212
+ const uniqueSupportLevel = new Set();
213
+
214
+ csvData.data.forEach(row => {
215
+ if (row.type) types.add(row.type);
216
+ if (row.support_level) uniqueSupportLevel.add(row.support_level);
217
+ });
71
218
 
72
- // Sort flatComponentsData alphabetically by name
73
- flatComponentsData.sort((a, b) => a.name.localeCompare(b.name));
219
+ const createOptions = (values) =>
220
+ Array.from(values)
221
+ .map(value => `<option selected value="${value}">${capitalize(value)}</option>`)
222
+ .join('');
74
223
 
75
224
  let tableHtml = `
76
- <div class="table-filters">
77
- <input class="table-search" type="text" id="componentTableSearch" onkeyup="filterComponentTable()" placeholder="Search for components...">
78
- <select class="type-dropdown" id="supportFilter" onchange="filterComponentTable()">
79
- <option value="">All Support</option>`;
80
-
81
- // Extract unique support values for the filter
82
- const uniqueSupportValues = [...new Set(Object.values(driverSupportData).flatMap(support => support.split(', ').map(pair => pair.split('=')[1])))];
83
- uniqueSupportValues.forEach(support => {
84
- tableHtml += `<option value="${support}">${support.charAt(0).toUpperCase() + support.slice(1)}</option>`;
85
- });
86
-
87
- tableHtml += `
88
- </select>
89
- <select class="type-dropdown" id="typeFilter" onchange="filterComponentTable()">
90
- <option value="">All Types</option>`;
225
+ <div class="table-filters">
226
+ <input class="table-search" type="text" id="componentTableSearch" onkeyup="filterComponentTable()" placeholder="Search for components...">
227
+ <label for="typeFilter">Type:</label>
228
+ <select multiple class="type-dropdown" id="typeFilter" onchange="filterComponentTable()">
229
+ ${createOptions(types)}
230
+ </select>
231
+ `
232
+ if (!isCloud) {
233
+ tableHtml += `
234
+ <br><label for="supportFilter" id="labelForSupportFilter">Support:</label>
235
+ <select multiple class="type-dropdown" id="supportFilter" onchange="filterComponentTable()">
236
+ ${createOptions(uniqueSupportLevel)}
237
+ </select>`
238
+ }
91
239
 
92
- // Extract unique types for the filter, only include input, processor, and output
93
- const uniqueTypes = [...new Set(flatComponentsData.flatMap(item => item.types.map(typeObj => typeObj.type)))].filter(type => ['input', 'processor', 'output'].includes(type));
94
- uniqueTypes.forEach(type => {
95
- tableHtml += `<option value="${type}">${type.charAt(0).toUpperCase() + type.slice(1)}</option>`;
96
- });
240
+ tableHtml += `</div>
241
+ <table class="tableblock frame-all grid-all stripes-even no-clip stretch component-table" id="componentTable">
242
+ <colgroup>
243
+ ${isCloud
244
+ ? '<col style="width: 50%;"><col style="width: 50%;">'
245
+ : '<col style="width: 25%;"><col style="width: 25%;"><col style="width: 25%;"><col style="width: 25%;">'
246
+ }
247
+ </colgroup>
248
+ <thead>
249
+ <tr>
250
+ <th class="tableblock halign-left valign-top">Name</th>
251
+ <th class="tableblock halign-left valign-top">Connector Type</th>
252
+ ${isCloud ? '' : `
253
+ <th class="tableblock halign-left valign-top">Support Level</th>
254
+ <th class="tableblock halign-left valign-top">Enterprise Licensed</th>`}
255
+ </tr>
256
+ </thead>
257
+ <tbody>
258
+ ${generateConnectorsHTMLTable(processConnectors(csvData), isCloud)}
259
+ </tbody>
260
+ </table>
261
+ <script>
262
+ ${filterComponentTable.toString()}
97
263
 
98
- tableHtml += `
99
- </select>
100
- </div>
101
- <table class="tableblock frame-all grid-all stripes-even no-clip stretch component-table" id="componentTable">
102
- <colgroup>
103
- <col style="width: 33.3%;">
104
- <col style="width: 33.3%;">
105
- <col style="width: 33.3%;">
106
- </colgroup>
107
- <thead>
108
- <tr>
109
- <th class="tableblock halign-left valign-top">Connector</th>
110
- <th class="tableblock halign-left valign-top">Support</th>
111
- <th class="tableblock halign-left valign-top">Type</th>
112
- </tr>
113
- </thead>
114
- <tbody>`;
115
-
116
- flatComponentsData.forEach(item => {
117
- const commonName = item.originalName !== item.name ? ` <small>(${item.name})</small>`: '';
118
- const isEnterprise = item.enterprise ? '<span class="enterprise-label" title="Requires an Enterprise Edition license">Enterprise</span>' : '';
119
- if (driverSupportData[item.originalName]) {
120
- const drivers = driverSupportData[item.originalName].split(', ');
121
- drivers.forEach(driverSupportPair => {
122
- const [driver, support] = driverSupportPair.split('=');
123
- // Find the common name
124
- const driverNameEntry = driverNameMap.find(driverItem => driverItem.key === driver)
125
- const driverName = driverNameEntry ? driverNameEntry.name : driver
126
-
127
- // Filter for types of input, processor, and output only
128
- const filteredTypes = item.types.filter(typeOption => ['input', 'processor', 'output'].includes(typeOption.type));
129
- if (filteredTypes.length > 0) {
130
- const typeDropdown = filteredTypes.length > 1
131
- ? `<select class="type-dropdown" onchange="updateComponentUrl(this, true)">
132
- ${filteredTypes.map(typeOption => `<option value="${typeOption.url}">${typeOption.type.charAt(0).toUpperCase() + typeOption.type.slice(1)}</option>`).join('')}
133
- </select>`
134
- : filteredTypes[0].type.charAt(0).toUpperCase() + filteredTypes[0].type.slice(1);
135
-
136
- tableHtml += `
137
- <tr>
138
- <td class="tableblock halign-left valign-top"><p class="tableblock"><p class="enterprise-label-container">${isEnterprise}</p><code><a href="${filteredTypes[0].url}">${item.originalName}</a></code> ${commonName}<br><span style="font-size:0.9rem;">${driverName} driver</span></p></td>
139
- <td class="tableblock halign-left valign-top"><p class="tableblock">${support.charAt(0).toUpperCase() + support.slice(1)}</p></td>
140
- <td class="tableblock halign-left valign-top"><p class="tableblock">${typeDropdown}</p></td>
141
- </tr>`;
142
- }
143
- });
144
- } else if (cacheSupportData[item.originalName]) {
145
- const caches = cacheSupportData[item.originalName].split(', ');
146
- caches.forEach(cacheSupportPair => {
147
- const [cache, support] = cacheSupportPair.split('=');
148
- // Find the common name
149
- const cacheNameEntry = cacheNameMap.find(cacheItem => cacheItem.key === cache)
150
- const cacheName = cacheNameEntry ? cacheNameEntry.name : cache
151
-
152
- // Filter for types of input, processor, and output only
153
- const filteredTypes = item.types.filter(typeOption => ['input', 'processor', 'output'].includes(typeOption.type));
154
- if (filteredTypes.length > 0) {
155
- const typeDropdown = filteredTypes.length > 1
156
- ? `<select class="type-dropdown" onchange="updateComponentUrl(this, true)">
157
- ${filteredTypes.map(typeOption => `<option value="${typeOption.url}">${typeOption.type.charAt(0).toUpperCase() + typeOption.type.slice(1)}</option>`).join('')}
158
- </select>`
159
- : filteredTypes[0].type.charAt(0).toUpperCase() + filteredTypes[0].type.slice(1);
160
-
161
- tableHtml += `
162
- <tr>
163
- <td class="tableblock halign-left valign-top"><p class="tableblock"><p class="enterprise-label-container">${isEnterprise}</p><code><a href="${filteredTypes[0].url}">${item.originalName}</a></code> ${commonName}<br><span style="font-size:0.9rem;">${cacheName}</span></p></td>
164
- <td class="tableblock halign-left valign-top"><p class="tableblock">${support.charAt(0).toUpperCase() + support.slice(1)}</p></td>
165
- <td class="tableblock halign-left valign-top"><p class="tableblock">${typeDropdown}</p></td>
166
- </tr>`;
167
- }
264
+ function getQueryParams() {
265
+ const params = {};
266
+ const searchParams = new URLSearchParams(window.location.search);
267
+ searchParams.forEach((value, key) => {
268
+ params[key] = value;
168
269
  });
169
- } else {
170
- // Filter for types of input, processor, and output only
171
- const filteredTypes = item.types.filter(typeOption => ['input', 'processor', 'output'].includes(typeOption.type));
172
- if (filteredTypes.length > 0) {
173
- const typeDropdown = filteredTypes.length > 1
174
- ? `<select class="type-dropdown" onchange="updateComponentUrl(this, true)">
175
- ${filteredTypes.map(typeObj => `<option value="${typeObj.url}">${typeObj.type.charAt(0).toUpperCase() + typeObj.type.slice(1)}</option>`).join('')}
176
- </select>`
177
- : filteredTypes[0].type.charAt(0).toUpperCase() + filteredTypes[0].type.slice(1);
178
-
179
- tableHtml += `
180
- <tr>
181
- <td class="tableblock halign-left valign-top"><p class="tableblock"><p class="enterprise-label-container">${isEnterprise}</p><code><a href="${filteredTypes[0].url}">${item.originalName}</a></code> ${commonName}</p></td>
182
- <td class="tableblock halign-left valign-top"><p class="tableblock">${item.support.charAt(0).toUpperCase() + item.support.slice(1)}</p></td>
183
- <td class="tableblock halign-left valign-top"><p class="tableblock">${typeDropdown}</p></td>
184
- </tr>`;
185
- }
270
+ return params;
186
271
  }
187
- });
188
272
 
189
- tableHtml += `
190
- </tbody>
191
- </table>
192
- <script>
193
- function filterComponentTable() {
194
- const nameInput = document.getElementById('componentTableSearch').value.toLowerCase();
195
- const supportFilter = document.getElementById('supportFilter').value;
196
- const typeFilter = document.getElementById('typeFilter').value;
197
- const table = document.getElementById('componentTable');
198
- const trs = table.getElementsByTagName('tr');
199
-
200
- for (let i = 1; i < trs.length; i++) {
201
- const nameTd = trs[i].getElementsByTagName('td')[0];
202
- const supportTd = trs[i].getElementsByTagName('td')[1];
203
- const typeTd = trs[i].getElementsByTagName('td')[2];
204
- const typeDropdown = typeTd.querySelector('.type-dropdown');
205
- let showRow =
206
- (!nameInput || nameTd.textContent.toLowerCase().includes(nameInput)) &&
207
- (!supportFilter || supportTd.textContent.toLowerCase() === supportFilter.toLowerCase()) &&
208
- (!typeFilter || (typeDropdown ? Array.from(typeDropdown.options).some(option => option.text.toLowerCase() === typeFilter.toLowerCase()) : typeTd.textContent.toLowerCase().includes(typeFilter.toLowerCase())));
209
-
210
- trs[i].style.display = showRow ? '' : 'none';
211
-
212
- if (showRow && typeFilter && typeDropdown) {
213
- const matchingOption = Array.from(typeDropdown.options).find(option => option.text.toLowerCase() === typeFilter.toLowerCase());
214
- typeDropdown.value = matchingOption.value;
215
- updateComponentUrl(typeDropdown, false);
273
+ function updateComponentUrl(select, redirect) {
274
+ const anchor = select.closest('tr').querySelector('a');
275
+ anchor.href = select.value;
276
+ if (redirect) {
277
+ window.location.href = select.value; // Redirect to the new URL
216
278
  }
217
279
  }
218
- }
219
-
220
- function getQueryParams() {
221
- const params = {};
222
- const searchParams = new URLSearchParams(window.location.search);
223
- searchParams.forEach((value, key) => {
224
- params[key] = value;
225
- });
226
- return params;
227
- }
228
-
229
- function capitalizeFirstLetter(string) {
230
- return string.charAt(0).toUpperCase() + string.slice(1);
231
- }
232
-
233
- function updateComponentUrl(select, redirect) {
234
- const anchor = select.closest('tr').querySelector('a');
235
- anchor.href = select.value;
236
- if (redirect) {
237
- window.location.href = select.value; // Redirect to the new URL
238
- }
239
- }
240
280
 
241
- // Initialize Choices.js for type dropdowns
242
- document.addEventListener('DOMContentLoaded', function() {
281
+ // Initialize Choices.js for type dropdowns
282
+ document.addEventListener('DOMContentLoaded', function() {
243
283
  const params = getQueryParams();
244
284
  if (params.search) {
245
285
  document.getElementById('componentTableSearch').value = params.search;
246
286
  }
247
- if (params.support) {
248
- document.getElementById('supportFilter').value = params.support;
249
- }
250
287
  if (params.type) {
251
288
  document.getElementById('typeFilter').value = params.type;
252
289
  }
290
+ if (params.support) {
291
+ document.getElementById('supportFilter').value = params.support;
292
+ }
293
+
253
294
  filterComponentTable();
254
295
  const typeDropdowns = document.querySelectorAll('.type-dropdown');
255
296
  typeDropdowns.forEach(dropdown => {
256
- new Choices(dropdown, { searchEnabled: false, allowHTML: true });
297
+ new Choices(dropdown, {
298
+ searchEnabled: false,
299
+ allowHTML: true,
300
+ removeItemButton: true });
301
+ });
257
302
  });
258
- });
259
- </script>`;
303
+ </script>`;
260
304
 
261
305
  return self.createBlock(parent, 'pass', tableHtml);
262
306
  });
263
307
  });
264
- // Add the block macro for displaying a dropdown of other supported types
308
+
265
309
  registry.blockMacro(function () {
266
310
  const self = this;
267
311
  self.named('component_type_dropdown');
@@ -274,16 +318,31 @@ module.exports.register = function (registry, context) {
274
318
  return self.createBlock(parent, 'pass', '');
275
319
  }
276
320
 
277
- const flatComponentsData = context.config?.attributes?.flatComponentsData || []
278
- const component = flatComponentsData.find(item => item.originalName === name);
321
+ const csvData = context.config?.attributes?.csvData || [];
322
+ const componentRows = csvData.data.filter(row => row.connector.trim().toLowerCase() === name.trim().toLowerCase());
279
323
 
280
- if (!component) {
324
+ if (componentRows.length === 0) {
281
325
  return self.createBlock(parent, 'pass', '');
282
326
  }
283
327
 
284
- // Check if the component requires an Enterprise license
328
+ // Process types from CSV
329
+ const types = componentRows.map(row => ({
330
+ type: row.type.trim(),
331
+ support: row.support_level.trim(),
332
+ url: row.url ? row.url.trim() : '#'
333
+ }));
334
+
335
+ // Move the current page's type to the first position in the dropdown
336
+ const sortedTypes = [...types];
337
+ const currentTypeIndex = sortedTypes.findIndex(typeObj => typeObj.type === type);
338
+ if (currentTypeIndex !== -1) {
339
+ const [currentType] = sortedTypes.splice(currentTypeIndex, 1);
340
+ sortedTypes.unshift(currentType);
341
+ }
342
+
343
+ // Check if the component requires an Enterprise license (based on support level)
285
344
  let enterpriseAdmonition = '';
286
- if (component.enterprise) {
345
+ if (componentRows.some(row => row.support_level.toLowerCase() === 'enterprise')) {
287
346
  enterpriseAdmonition = `
288
347
  <div class="admonitionblock note">
289
348
  <table>
@@ -303,20 +362,14 @@ module.exports.register = function (registry, context) {
303
362
  </div>`;
304
363
  }
305
364
 
306
- // Move the page's current type to the first position in the dropdown
307
- const sortedTypes = [...component.types];
308
- const currentTypeIndex = sortedTypes.findIndex(typeObj => typeObj.type === type);
309
- if (currentTypeIndex !== -1) {
310
- const [currentType] = sortedTypes.splice(currentTypeIndex, 1);
311
- sortedTypes.unshift(currentType);
312
- }
365
+ // Create the dropdown for types
313
366
  let typeDropdown = '';
314
- if (component.types.length > 1) {
367
+ if (sortedTypes.length > 1) {
315
368
  typeDropdown = `
316
369
  <div class="page-type-dropdown">
317
370
  <p>Type: </p>
318
371
  <select class="type-dropdown" onchange="window.location.href=this.value">
319
- ${sortedTypes.map(typeObj => `<option value="${typeObj.url}" data-support="${typeObj.support}">${typeObj.type.charAt(0).toUpperCase() + typeObj.type.slice(1)}</option>`).join('')}
372
+ ${sortedTypes.map(typeObj => `<option value="../${typeObj.url}/" data-support="${typeObj.support}">${capitalize(typeObj.type)}</option>`).join('')}
320
373
  </select>
321
374
  </div>
322
375
  <script>
@@ -332,4 +385,4 @@ module.exports.register = function (registry, context) {
332
385
  return self.createBlock(parent, 'pass', typeDropdown + enterpriseAdmonition);
333
386
  });
334
387
  });
335
- };
388
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redpanda-data/docs-extensions-and-macros",
3
- "version": "3.5.10",
3
+ "version": "3.6.0",
4
4
  "description": "Antora extensions and macros developed for Redpanda documentation.",
5
5
  "keywords": [
6
6
  "antora",
@@ -31,6 +31,7 @@
31
31
  },
32
32
  "./extensions/replace-attributes-in-attachments": "./extensions/replace-attributes-in-attachments.js",
33
33
  "./extensions/generate-rp-connect-categories": "./extensions/generate-rp-connect-categories.js",
34
+ "./extensions/generate-rp-connect-info": "./extensions/generate-rp-connect-info.js",
34
35
  "./extensions/add-global-attributes": "./extensions/add-global-attributes.js",
35
36
  "./extensions/version-fetcher/set-latest-version": "./extensions/version-fetcher/set-latest-version.js",
36
37
  "./extensions/modify-connect-tag-playbook": "./extensions/modify-connect-tag-playbook.js",
@@ -57,6 +58,9 @@
57
58
  "url": "git+https://github.com/redpanda-data/docs-extensions-and-macros"
58
59
  },
59
60
  "dependencies": {
61
+ "@octokit/core": "^6.1.2",
62
+ "@octokit/plugin-retry": "^7.1.1",
63
+ "@octokit/rest": "^21.0.1",
60
64
  "algoliasearch": "^4.17.0",
61
65
  "chalk": "4.1.2",
62
66
  "gulp": "^4.0.2",
@@ -65,14 +69,12 @@
65
69
  "js-yaml": "^4.1.0",
66
70
  "lodash": "^4.17.21",
67
71
  "node-html-parser": "5.4.2-0",
72
+ "papaparse": "^5.4.1",
68
73
  "semver": "^7.6.0"
69
74
  },
70
75
  "devDependencies": {
71
76
  "@antora/cli": "3.1.4",
72
77
  "@antora/site-generator": "3.1.4",
73
- "@octokit/core": "^6.1.2",
74
- "@octokit/plugin-retry": "^7.1.1",
75
- "@octokit/rest": "^21.0.1",
76
78
  "@web/dev-server": "^0.2.5"
77
79
  }
78
80
  }