@redpanda-data/docs-extensions-and-macros 3.3.1 → 3.3.3

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.
@@ -175,7 +175,7 @@ function generateIndex (playbook, contentCatalog, { indexLatestOnly = false, exc
175
175
  .replace(/\s+/g, ' ')
176
176
  .trim();
177
177
 
178
- var tag = `${component.title} ${version ? 'v' + version : ''}`
178
+ var tag = `${component.title} ${version ? 'v' + version : ''}`.trim();
179
179
  var indexItem;
180
180
  const deployment = page.asciidoc?.attributes['env-kubernetes'] ? 'Kubernetes' : page.asciidoc?.attributes['env-linux'] ? 'Linux' : page.asciidoc?.attributes['env-docker'] ? 'Docker' : page.asciidoc?.attributes['page-cloud'] ? 'Redpanda Cloud' : ''
181
181
 
@@ -3,8 +3,7 @@
3
3
  module.exports.register = function ({ config }) {
4
4
  const logger = this.getLogger('redpanda-connect-category-aggregation-extension');
5
5
 
6
- // TODO: Integrate these as attributes like common-name in the autogenerated source pages.
7
- // This hardcoded map is just for launch.
6
+ // Component name mapping for common names
8
7
  const componentNameMap = {
9
8
  "aws_kinesis_data_streams": "AWS Kinesis Data Streams",
10
9
  "aws_kinesis_firehose": "AWS Kinesis Firehose",
@@ -46,7 +45,6 @@ module.exports.register = function ({ config }) {
46
45
  "mongodb": "MongoDB",
47
46
  "mqtt": "MQTT",
48
47
  "mysql": "MySQL",
49
- "nanomsg": "nanomsg",
50
48
  "nats": "NATS",
51
49
  "nsq": "NSQ",
52
50
  "opensearch": "OpenSearch",
@@ -58,22 +56,90 @@ module.exports.register = function ({ config }) {
58
56
  "ristretto": "Ristretto",
59
57
  "schema_registry": "Schema Registry",
60
58
  "sentry": "Sentry",
59
+ "splunk_hec": "Splunk",
60
+ "snowflake_put": "Snowflake",
61
61
  "socket": "Socket",
62
62
  "sql": "SQL",
63
63
  "sqlite": "SQLite",
64
64
  "trino": "Trino",
65
- "wasm": "Wasm (WebAssembly)",
65
+ "wasm": "WebAssembly",
66
66
  "websocket": "WebSocket",
67
- "twitter_search": "X (Twitter)",
67
+ "twitter_search": "X/Twitter",
68
68
  "xml": "XML"
69
69
  };
70
70
 
71
+ const certifiedConnectors = {
72
+ "amqp_0_9": ["input", "output"],
73
+ "archive": ["processor"],
74
+ "aws_dynamodb_partiql": ["processor"],
75
+ "aws_kinesis": ["input", "output"],
76
+ "aws_kinesis_firehose": ["output"],
77
+ "aws_lambda": ["processor"],
78
+ "aws_s3": ["input", "output"],
79
+ "aws_sqs": ["input", "output"],
80
+ "bloblang": ["processor"],
81
+ "bounds_check": ["processor"],
82
+ "cache": ["processor"],
83
+ "cached": ["processor"],
84
+ "command": ["processor"],
85
+ "compress": ["processor"],
86
+ "csv": ["input"],
87
+ "decompress": ["processor"],
88
+ "dedupe": ["processor"],
89
+ "file": ["input", "output"],
90
+ "generate": ["input"],
91
+ "group_by": ["processor"],
92
+ "group_by_value": ["processor"],
93
+ "http": ["processor"],
94
+ "http_client": ["input", "output"],
95
+ "http_server": ["input", "output"],
96
+ "javascript": ["processor"],
97
+ "jmespath": ["processor"],
98
+ "jq": ["processor"],
99
+ "json_schema": ["processor"],
100
+ "kafka": ["input", "output"],
101
+ "kafka_franz": ["input", "output"],
102
+ "log": ["processor"],
103
+ "mapping": ["processor"],
104
+ "metric": ["processor"],
105
+ "mutation": ["processor"],
106
+ "nats": ["input", "output"],
107
+ "nats_jetstream": ["input", "output"],
108
+ "nats_kv": ["input", "output", "processor"],
109
+ "nats_request_reply": ["processor"],
110
+ "opensearch": ["output"],
111
+ "parquet_decode": ["processor"],
112
+ "parquet_encode": ["processor"],
113
+ "protobuf": ["processor"],
114
+ "rate_limit": ["processor"],
115
+ "redis": ["processor"],
116
+ "redis_hash": ["output"],
117
+ "redis_list": ["input", "output"],
118
+ "redis_pubsub": ["input", "output"],
119
+ "redis_script": ["processor"],
120
+ "redis_streams": ["input", "output"],
121
+ "schema_registry_decode": ["processor"],
122
+ "schema_registry_encode": ["processor"],
123
+ "select_parts": ["processor"],
124
+ "sftp": ["output"],
125
+ "sleep": ["processor"],
126
+ "socket": ["input", "output"],
127
+ "socket_server": ["input", "output"],
128
+ "sql_insert": ["output", "processor"],
129
+ "sql_raw": ["input", "output", "processor"],
130
+ "sql_select": ["input", "output", "processor"],
131
+ "unarchive": ["processor"],
132
+ "websocket": ["input", "output"],
133
+ "workflow": ["processor"]
134
+ };
135
+
71
136
  this.once('contentClassified', ({ siteCatalog, contentCatalog }) => {
72
137
  const redpandaConnect = contentCatalog.getComponents().find(component => component.name === 'redpanda-connect');
73
138
  if (!redpandaConnect || !redpandaConnect.latest) {
74
139
  logger.info('Could not find the redpanda-connect component');
75
140
  return;
76
141
  }
142
+
77
143
  const descriptions = redpandaConnect.latest.asciidoc.attributes.categories;
78
144
  if (!descriptions) {
79
145
  logger.info('No categories attribute found in redpanda-connect component');
@@ -82,6 +148,8 @@ module.exports.register = function ({ config }) {
82
148
 
83
149
  const connectCategoriesData = {};
84
150
  const flatComponentsData = [];
151
+ const driverSupportData = {};
152
+ const cacheSupportData = {};
85
153
  const types = Object.keys(descriptions);
86
154
 
87
155
  // Initialize connectCategoriesData for each type
@@ -93,32 +161,38 @@ module.exports.register = function ({ config }) {
93
161
  const files = contentCatalog.findBy({ component: 'redpanda-connect', family: 'page' });
94
162
 
95
163
  files.forEach(file => {
96
- const content = file.contents.toString('utf8');
164
+ let content = file.contents.toString('utf8');
97
165
  const categoryMatch = /:categories: (.*)/.exec(content);
98
166
  const typeMatch = /:type: (.*)/.exec(content);
99
167
  const statusMatch = /:status: (.*)/.exec(content);
168
+ const driverSupportMatch = /:driver-support: (.*)/.exec(content);
169
+ const cacheSupportMatch = /:cache-support: (.*)/.exec(content);
170
+ const enterpriseMatch = /:enterprise: true/.exec(content);
100
171
  const pubUrl = file.pub.url;
101
172
  const name = file.src.stem;
102
173
 
103
- if (categoryMatch && typeMatch) {
104
- let categories = categoryMatch[1];
174
+ if (typeMatch) {
105
175
  const fileType = typeMatch[1];
106
- let status = statusMatch ? statusMatch[1] : 'Enterprise';
107
- if (status === 'beta' || status === 'experimental') status = 'Community';
108
- if (status === 'stable') status = 'Enterprise';
176
+
177
+ let status = statusMatch ? statusMatch[1] : 'community';
178
+ //if (status === 'beta' || status === 'experimental') status = 'community';
179
+ //if (status === 'stable') status = 'certified';
109
180
 
110
181
  // Skip deprecated components
111
182
  if (status === 'deprecated') return;
112
183
 
113
- // Map component name to common name
114
- const commonName = componentNameMap[name] || name;
184
+ // Override status to "certified" if in the lookup table
185
+ if (certifiedConnectors[name] && certifiedConnectors[name].includes(fileType) || enterpriseMatch) {
186
+ status = 'certified';
187
+ } else {
188
+ status = 'community';
189
+ }
190
+
191
+ const commonName = componentNameMap?.[name] ?? name;
115
192
 
116
193
  // Populate connectCategoriesData
117
- if (types.includes(fileType) && categories) {
118
- if (typeof categories === 'string') {
119
- categories = categories.replace(/[\[\]"]/g, '').split(',').map(category => category.trim());
120
- }
121
-
194
+ if (types.includes(fileType) && categoryMatch) {
195
+ const categories = categoryMatch[1].replace(/[\[\]"]/g, '').split(',').map(category => category.trim());
122
196
  categories.forEach(category => {
123
197
  let categoryObj = connectCategoriesData[fileType].find(cat => cat.name === category);
124
198
 
@@ -134,26 +208,29 @@ module.exports.register = function ({ config }) {
134
208
 
135
209
  // Populate flatComponentsData
136
210
  let flatItem = flatComponentsData.find(item => item.name === commonName);
137
- if (flatItem) {
138
- if (!flatItem.types.includes(fileType)) {
139
- flatItem.types.push({ type: fileType, url: pubUrl });
140
- }
141
- } else {
142
- flatItem = { name: commonName, originalName: name, support: status, types: [{ type: fileType, url: pubUrl }] };
211
+ if (!flatItem) {
212
+ flatItem = { name: commonName, originalName: name, support: status, types: [], enterprise: enterpriseMatch ? true : false};
143
213
  flatComponentsData.push(flatItem);
144
214
  }
215
+
216
+ if (!flatItem.types.some(type => type.type === fileType)) {
217
+ flatItem.types.push({ type: fileType, url: pubUrl, enterprise: enterpriseMatch? true : false, support: status});
218
+ }
219
+
220
+ // Populate support data
221
+ if (driverSupportMatch) driverSupportData[name] = driverSupportMatch[1];
222
+ if (cacheSupportMatch) cacheSupportData[name] = cacheSupportMatch[1];
145
223
  }
146
224
  });
147
225
 
148
- try {
149
- redpandaConnect.latest.asciidoc.attributes.connectCategoriesData = connectCategoriesData;
150
- redpandaConnect.latest.asciidoc.attributes.flatComponentsData = flatComponentsData;
151
- logger.info(`Added Redpanda Connect data to latest Asciidoc object: ${JSON.stringify({ connectCategoriesData, flatComponentsData }, null, 2)}`);
152
- } catch (error) {
153
- logger.error(`Error updating latest Asciidoc object for Redpanda Connect: ${error.message}`);
154
- }
226
+ redpandaConnect.latest.asciidoc.attributes.connectCategoriesData = connectCategoriesData;
227
+ redpandaConnect.latest.asciidoc.attributes.flatComponentsData = flatComponentsData;
228
+ redpandaConnect.latest.asciidoc.attributes.driverSupportData = driverSupportData;
229
+ redpandaConnect.latest.asciidoc.attributes.cacheSupportData = cacheSupportData;
230
+
231
+ logger.info(`Added Redpanda Connect data to latest Asciidoc object: ${JSON.stringify({ connectCategoriesData, flatComponentsData }, null, 2)}`);
155
232
  } catch (error) {
156
233
  logger.error(`Error processing Redpanda Connect files: ${error.message}`);
157
234
  }
158
235
  });
159
- };
236
+ };
@@ -0,0 +1,11 @@
1
+ 'use strict';
2
+
3
+ module.exports.register = function (registry) {
4
+ registry.inlineMacro(function () {
5
+ const self = this;
6
+ self.named('enterprise-label');
7
+ self.process((parent, target, attrs) => {
8
+ return `<span class="inline-enterprise-label">Enterprise</span>`;
9
+ });
10
+ });
11
+ };
@@ -3,6 +3,38 @@
3
3
  module.exports.register = function (registry, context) {
4
4
  let tabsCounter = 1; // Counter for generating unique IDs
5
5
 
6
+ const driverNameMap = {
7
+ "gocosmos": "Azure Cosmos DB",
8
+ "clickhouse": "ClickHouse",
9
+ "oracle": "Oracle",
10
+ "mssql": "Microsoft SQL Server",
11
+ "mysql": "MYSQL",
12
+ "snowflake": "Snowflake",
13
+ "postgresql": "PostgreSQL",
14
+ "postgres": "PostgreSQL",
15
+ "sqlite": "SQLite",
16
+ "trino": "Trino",
17
+ }
18
+
19
+ const cacheNameMap = {
20
+ "aws_dynamodb": "AWS DynamoDB",
21
+ "memcached": "Memcached",
22
+ "redis": "Redis",
23
+ "aws_s3": "AWS S3",
24
+ "memory": "Memory",
25
+ "ristretto": "Ristretto",
26
+ "couchbase": "Couchbase",
27
+ "mongodb": "MongoDB",
28
+ "sql": "SQL",
29
+ "file": "File",
30
+ "multilevel": "Multilevel",
31
+ "ttlru": "TTL LRU",
32
+ "gcp_cloud_storage": "GCP Cloud Storage",
33
+ "nats_kv": "NATS KV",
34
+ "lru": "LRU",
35
+ "noop": "Noop",
36
+ };
37
+
6
38
  // Add the category tabs for components
7
39
  registry.blockMacro(function () {
8
40
  const self = this;
@@ -11,8 +43,9 @@ module.exports.register = function (registry, context) {
11
43
  self.process((parent, target, attrs) => {
12
44
  const type = attrs.type;
13
45
  const categoriesData = context.config?.attributes?.connectCategoriesData || {}
14
- const categories = categoriesData[type] || {};
46
+ const categories = categoriesData[type] || null;
15
47
  const currentTabsId = `tabs-${tabsCounter++}`; // Unique ID for this set of tabs
48
+ if (!categories) return
16
49
 
17
50
  let tabsHtml = `
18
51
  <div id="${currentTabsId}" class="openblock tabs is-sync is-loaded" data-sync-group-id="${type}">
@@ -62,136 +95,196 @@ module.exports.register = function (registry, context) {
62
95
  const self = this;
63
96
  self.named('component_table');
64
97
  self.process((parent, target, attrs) => {
65
- const flatComponentsData = context.config?.attributes?.flatComponentsData || []
98
+ const flatComponentsData = context.config?.attributes?.flatComponentsData || [];
99
+ const driverSupportData = context.config?.attributes?.driverSupportData || {};
100
+ const cacheSupportData = context.config?.attributes?.cacheSupportData || {};
66
101
 
67
102
  // Sort flatComponentsData alphabetically by name
68
103
  flatComponentsData.sort((a, b) => a.name.localeCompare(b.name));
69
104
 
70
105
  let tableHtml = `
71
- <div class="table-filters">
72
- <input class="table-search" type="text" id="componentTableSearch" onkeyup="filterComponentTable()" placeholder="Search for components...">
73
- <select class="type-dropdown" id="supportFilter" onchange="filterComponentTable()">
74
- <option value="">All Support</option>`;
106
+ <div class="table-filters">
107
+ <input class="table-search" type="text" id="componentTableSearch" onkeyup="filterComponentTable()" placeholder="Search for components...">
108
+ <select class="type-dropdown" id="supportFilter" onchange="filterComponentTable()">
109
+ <option value="">All Support</option>`;
75
110
 
76
111
  // Extract unique support values for the filter
77
- const uniqueSupportValues = [...new Set(flatComponentsData.map(item => item.support))];
112
+ const uniqueSupportValues = [...new Set(Object.values(driverSupportData).flatMap(support => support.split(', ').map(pair => pair.split('=')[1])))];
78
113
  uniqueSupportValues.forEach(support => {
79
- tableHtml += `<option value="${support}">${support}</option>`;
114
+ tableHtml += `<option value="${support}">${support.charAt(0).toUpperCase() + support.slice(1)}</option>`;
80
115
  });
81
116
 
82
117
  tableHtml += `
83
- </select>
84
- <select class="type-dropdown" id="typeFilter" onchange="filterComponentTable()">
85
- <option value="">All Types</option>`;
118
+ </select>
119
+ <select class="type-dropdown" id="typeFilter" onchange="filterComponentTable()">
120
+ <option value="">All Types</option>`;
86
121
 
87
- // Extract unique types for the filter
88
- const uniqueTypes = [...new Set(flatComponentsData.flatMap(item => item.types.map(typeObj => typeObj.type)))];
122
+ // Extract unique types for the filter, only include input, processor, and output
123
+ const uniqueTypes = [...new Set(flatComponentsData.flatMap(item => item.types.map(typeObj => typeObj.type)))].filter(type => ['input', 'processor', 'output'].includes(type));
89
124
  uniqueTypes.forEach(type => {
90
125
  tableHtml += `<option value="${type}">${type.charAt(0).toUpperCase() + type.slice(1)}</option>`;
91
126
  });
92
127
 
93
128
  tableHtml += `
94
- </select>
95
- </div>
96
- <table class="tableblock frame-all grid-all stripes-even no-clip stretch component-table" id="componentTable">
97
- <colgroup>
98
- <col style="width: 33.3%;">
99
- <col style="width: 33.3%;">
100
- <col style="width: 33.3%;">
101
- </colgroup>
102
- <thead>
103
- <tr>
104
- <th class="tableblock halign-left valign-top">Connector</th>
105
- <th class="tableblock halign-left valign-top">Support</th>
106
- <th class="tableblock halign-left valign-top">Type</th>
107
- </tr>
108
- </thead>
109
- <tbody>`;
129
+ </select>
130
+ </div>
131
+ <table class="tableblock frame-all grid-all stripes-even no-clip stretch component-table" id="componentTable">
132
+ <colgroup>
133
+ <col style="width: 33.3%;">
134
+ <col style="width: 33.3%;">
135
+ <col style="width: 33.3%;">
136
+ </colgroup>
137
+ <thead>
138
+ <tr>
139
+ <th class="tableblock halign-left valign-top">Connector</th>
140
+ <th class="tableblock halign-left valign-top">Support</th>
141
+ <th class="tableblock halign-left valign-top">Type</th>
142
+ </tr>
143
+ </thead>
144
+ <tbody>`;
110
145
 
111
146
  flatComponentsData.forEach(item => {
112
- const typeDropdown = item.types.length > 1
113
- ? `<select class="type-dropdown" onchange="updateComponentUrl(this, true)">
114
- ${item.types.map(typeObj => `<option value="${typeObj.url}">${typeObj.type.charAt(0).toUpperCase() + typeObj.type.slice(1)}</option>`).join('')}
115
- </select>`
116
- : item.types[0].type;
117
-
118
- tableHtml += `
119
- <tr>
120
- <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="${item.types[0].url}">${item.name}</a></p></td>
121
- <td class="tableblock halign-left valign-top"><p class="tableblock">${item.support}</p></td>
122
- <td class="tableblock halign-left valign-top"><p class="tableblock">${typeDropdown}</p></td>
123
- </tr>`;
147
+ const commonName = item.originalName !== item.name ? ` <small>(${item.name})</small>`: '';
148
+ const isEnterprise = item.enterprise ? '<span class="enterprise-label" title="Requires an Enterprise Edition license">Enterprise</span>' : '';
149
+ if (driverSupportData[item.originalName]) {
150
+ const drivers = driverSupportData[item.originalName].split(', ');
151
+ drivers.forEach(driverSupportPair => {
152
+ const [driver, support] = driverSupportPair.split('=');
153
+ // Map driver name to common name
154
+ const driverName = driverNameMap[driver] || driver;
155
+
156
+ // Filter for types of input, processor, and output only
157
+ const filteredTypes = item.types.filter(typeOption => ['input', 'processor', 'output'].includes(typeOption.type));
158
+ if (filteredTypes.length > 0) {
159
+ const typeDropdown = filteredTypes.length > 1
160
+ ? `<select class="type-dropdown" onchange="updateComponentUrl(this, true)">
161
+ ${filteredTypes.map(typeOption => `<option value="${typeOption.url}">${typeOption.type.charAt(0).toUpperCase() + typeOption.type.slice(1)}</option>`).join('')}
162
+ </select>`
163
+ : filteredTypes[0].type.charAt(0).toUpperCase() + filteredTypes[0].type.slice(1);
164
+
165
+ tableHtml += `
166
+ <tr>
167
+ <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>
168
+ <td class="tableblock halign-left valign-top"><p class="tableblock">${support.charAt(0).toUpperCase() + support.slice(1)}</p></td>
169
+ <td class="tableblock halign-left valign-top"><p class="tableblock">${typeDropdown}</p></td>
170
+ </tr>`;
171
+ }
172
+ });
173
+ } else if (cacheSupportData[item.originalName]) {
174
+ const caches = cacheSupportData[item.originalName].split(', ');
175
+ caches.forEach(cacheSupportPair => {
176
+ const [cache, support] = cacheSupportPair.split('=');
177
+ // Map driver name to common name
178
+ const cacheName = cacheNameMap[cache] || cache;
179
+
180
+ // Filter for types of input, processor, and output only
181
+ const filteredTypes = item.types.filter(typeOption => ['input', 'processor', 'output'].includes(typeOption.type));
182
+ if (filteredTypes.length > 0) {
183
+ const typeDropdown = filteredTypes.length > 1
184
+ ? `<select class="type-dropdown" onchange="updateComponentUrl(this, true)">
185
+ ${filteredTypes.map(typeOption => `<option value="${typeOption.url}">${typeOption.type.charAt(0).toUpperCase() + typeOption.type.slice(1)}</option>`).join('')}
186
+ </select>`
187
+ : filteredTypes[0].type.charAt(0).toUpperCase() + filteredTypes[0].type.slice(1);
188
+
189
+ tableHtml += `
190
+ <tr>
191
+ <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>
192
+ <td class="tableblock halign-left valign-top"><p class="tableblock">${support.charAt(0).toUpperCase() + support.slice(1)}</p></td>
193
+ <td class="tableblock halign-left valign-top"><p class="tableblock">${typeDropdown}</p></td>
194
+ </tr>`;
195
+ }
196
+ });
197
+ } else {
198
+ // Filter for types of input, processor, and output only
199
+ const filteredTypes = item.types.filter(typeOption => ['input', 'processor', 'output'].includes(typeOption.type));
200
+ if (filteredTypes.length > 0) {
201
+ const typeDropdown = filteredTypes.length > 1
202
+ ? `<select class="type-dropdown" onchange="updateComponentUrl(this, true)">
203
+ ${filteredTypes.map(typeObj => `<option value="${typeObj.url}">${typeObj.type.charAt(0).toUpperCase() + typeObj.type.slice(1)}</option>`).join('')}
204
+ </select>`
205
+ : filteredTypes[0].type.charAt(0).toUpperCase() + filteredTypes[0].type.slice(1);
206
+
207
+ tableHtml += `
208
+ <tr>
209
+ <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>
210
+ <td class="tableblock halign-left valign-top"><p class="tableblock">${item.support.charAt(0).toUpperCase() + item.support.slice(1)}</p></td>
211
+ <td class="tableblock halign-left valign-top"><p class="tableblock">${typeDropdown}</p></td>
212
+ </tr>`;
213
+ }
214
+ }
124
215
  });
125
216
 
126
217
  tableHtml += `
127
- </tbody>
128
- </table>
129
- <script>
130
- function filterComponentTable() {
131
- const nameInput = document.getElementById('componentTableSearch').value.toLowerCase();
132
- const supportFilter = document.getElementById('supportFilter').value;
133
- const typeFilter = document.getElementById('typeFilter').value;
134
- const table = document.getElementById('componentTable');
135
- const trs = table.getElementsByTagName('tr');
136
-
137
- for (let i = 1; i < trs.length; i++) {
138
- const nameTd = trs[i].getElementsByTagName('td')[0];
139
- const supportTd = trs[i].getElementsByTagName('td')[1];
140
- const typeTd = trs[i].getElementsByTagName('td')[2];
141
- const typeDropdown = typeTd.querySelector('.type-dropdown');
142
- let showRow =
143
- (!nameInput || nameTd.textContent.toLowerCase().includes(nameInput)) &&
144
- (!supportFilter || supportTd.textContent === supportFilter) &&
145
- (!typeFilter || (typeDropdown ? Array.from(typeDropdown.options).some(option => option.text.toLowerCase() === typeFilter.toLowerCase()) : typeTd.textContent.toLowerCase().includes(typeFilter.toLowerCase())));
146
-
147
- trs[i].style.display = showRow ? '' : 'none';
148
-
149
- if (showRow && typeFilter && typeDropdown) {
150
- const matchingOption = Array.from(typeDropdown.options).find(option => option.text.toLowerCase() === typeFilter.toLowerCase());
151
- typeDropdown.value = matchingOption.value;
152
- updateComponentUrl(typeDropdown, false);
153
- }
154
- }
155
- }
156
- function getQueryParams() {
157
- const params = {};
158
- const searchParams = new URLSearchParams(window.location.search);
159
- searchParams.forEach((value, key) => {
160
- params[key] = value;
161
- });
162
- return params;
163
- }
218
+ </tbody>
219
+ </table>
220
+ <script>
221
+ function filterComponentTable() {
222
+ const nameInput = document.getElementById('componentTableSearch').value.toLowerCase();
223
+ const supportFilter = document.getElementById('supportFilter').value;
224
+ const typeFilter = document.getElementById('typeFilter').value;
225
+ const table = document.getElementById('componentTable');
226
+ const trs = table.getElementsByTagName('tr');
164
227
 
165
- function capitalizeFirstLetter(string) {
166
- return string.charAt(0).toUpperCase() + string.slice(1);
167
- }
228
+ for (let i = 1; i < trs.length; i++) {
229
+ const nameTd = trs[i].getElementsByTagName('td')[0];
230
+ const supportTd = trs[i].getElementsByTagName('td')[1];
231
+ const typeTd = trs[i].getElementsByTagName('td')[2];
232
+ const typeDropdown = typeTd.querySelector('.type-dropdown');
233
+ let showRow =
234
+ (!nameInput || nameTd.textContent.toLowerCase().includes(nameInput)) &&
235
+ (!supportFilter || supportTd.textContent.toLowerCase() === supportFilter.toLowerCase()) &&
236
+ (!typeFilter || (typeDropdown ? Array.from(typeDropdown.options).some(option => option.text.toLowerCase() === typeFilter.toLowerCase()) : typeTd.textContent.toLowerCase().includes(typeFilter.toLowerCase())));
168
237
 
169
- function updateComponentUrl(select, redirect) {
170
- const anchor = select.closest('tr').querySelector('a');
171
- anchor.href = select.value;
172
- if (redirect) {
173
- window.location.href = select.value; // Redirect to the new URL
174
- }
175
- }
176
- // Initialize Choices.js for type dropdowns
177
- document.addEventListener('DOMContentLoaded', function() {
178
- const params = getQueryParams();
179
- if (params.search) {
180
- document.getElementById('componentTableSearch').value = params.search;
181
- }
182
- if (params.support) {
183
- document.getElementById('supportFilter').value = capitalizeFirstLetter(params.support);
184
- }
185
- if (params.type) {
186
- document.getElementById('typeFilter').value = params.type;
187
- }
188
- filterComponentTable();
189
- const typeDropdowns = document.querySelectorAll('.type-dropdown');
190
- typeDropdowns.forEach(dropdown => {
191
- new Choices(dropdown, { searchEnabled: false, allowHTML: true});
192
- });
193
- });
194
- </script>`;
238
+ trs[i].style.display = showRow ? '' : 'none';
239
+
240
+ if (showRow && typeFilter && typeDropdown) {
241
+ const matchingOption = Array.from(typeDropdown.options).find(option => option.text.toLowerCase() === typeFilter.toLowerCase());
242
+ typeDropdown.value = matchingOption.value;
243
+ updateComponentUrl(typeDropdown, false);
244
+ }
245
+ }
246
+ }
247
+
248
+ function getQueryParams() {
249
+ const params = {};
250
+ const searchParams = new URLSearchParams(window.location.search);
251
+ searchParams.forEach((value, key) => {
252
+ params[key] = value;
253
+ });
254
+ return params;
255
+ }
256
+
257
+ function capitalizeFirstLetter(string) {
258
+ return string.charAt(0).toUpperCase() + string.slice(1);
259
+ }
260
+
261
+ function updateComponentUrl(select, redirect) {
262
+ const anchor = select.closest('tr').querySelector('a');
263
+ anchor.href = select.value;
264
+ if (redirect) {
265
+ window.location.href = select.value; // Redirect to the new URL
266
+ }
267
+ }
268
+
269
+ // Initialize Choices.js for type dropdowns
270
+ document.addEventListener('DOMContentLoaded', function() {
271
+ const params = getQueryParams();
272
+ if (params.search) {
273
+ document.getElementById('componentTableSearch').value = params.search;
274
+ }
275
+ if (params.support) {
276
+ document.getElementById('supportFilter').value = params.support;
277
+ }
278
+ if (params.type) {
279
+ document.getElementById('typeFilter').value = params.type;
280
+ }
281
+ filterComponentTable();
282
+ const typeDropdowns = document.querySelectorAll('.type-dropdown');
283
+ typeDropdowns.forEach(dropdown => {
284
+ new Choices(dropdown, { searchEnabled: false, allowHTML: true });
285
+ });
286
+ });
287
+ </script>`;
195
288
 
196
289
  return self.createBlock(parent, 'pass', tableHtml);
197
290
  });
@@ -206,17 +299,38 @@ module.exports.register = function (registry, context) {
206
299
  const type = attributes['type'];
207
300
 
208
301
  if (!name || !type) {
209
- console.log('Name or type attribute is missing');
210
302
  return self.createBlock(parent, 'pass', '');
211
303
  }
212
304
 
213
305
  const flatComponentsData = context.config?.attributes?.flatComponentsData || []
214
306
  const component = flatComponentsData.find(item => item.originalName === name);
215
307
 
216
- if (!component || component.types.length <= 1) {
308
+ if (!component) {
217
309
  return self.createBlock(parent, 'pass', '');
218
310
  }
219
311
 
312
+ // Check if the component requires an Enterprise license
313
+ let enterpriseAdmonition = '';
314
+ if (component.enterprise) {
315
+ enterpriseAdmonition = `
316
+ <div class="admonitionblock note">
317
+ <table>
318
+ <tbody>
319
+ <tr>
320
+ <td class="icon">
321
+ <i class="fa icon-note" title="Note"></i>
322
+ </td>
323
+ <td class="content">
324
+ <div class="paragraph">
325
+ <p>This feature requires an <a href="https://redpanda.com/compare-platform-editions" target="_blank">Enterprise license</a>. To upgrade, contact <a href="https://redpanda.com/try-redpanda?section=enterprise-trial" target="_blank" rel="noopener">Redpanda sales</a>.</p>
326
+ </div>
327
+ </td>
328
+ </tr>
329
+ </tbody>
330
+ </table>
331
+ </div>`;
332
+ }
333
+
220
334
  // Move the page's current type to the first position in the dropdown
221
335
  const sortedTypes = [...component.types];
222
336
  const currentTypeIndex = sortedTypes.findIndex(typeObj => typeObj.type === type);
@@ -224,25 +338,26 @@ module.exports.register = function (registry, context) {
224
338
  const [currentType] = sortedTypes.splice(currentTypeIndex, 1);
225
339
  sortedTypes.unshift(currentType);
226
340
  }
227
-
228
- const typeDropdown = `
229
- <div class="page-type-dropdown">
230
- <p>Type: </p>
231
- <select class="type-dropdown" onchange="window.location.href=this.value">
232
- ${sortedTypes.map(typeObj => `<option value="${typeObj.url}" data-support="${typeObj.support}">${typeObj.type.charAt(0).toUpperCase() + typeObj.type.slice(1)}</option>`).join('')}
233
- </select>
234
- </div>
235
- <script>
236
- // Initialize Choices.js for type dropdowns
237
- document.addEventListener('DOMContentLoaded', function() {
238
- const typeDropdowns = document.querySelectorAll('.type-dropdown');
239
- typeDropdowns.forEach(dropdown => {
240
- new Choices(dropdown, { searchEnabled: false, allowHTML: true, shouldSort: false });
241
- });
242
- });
243
- </script>`;
244
-
245
- return self.createBlock(parent, 'pass', typeDropdown);
341
+ let typeDropdown = '';
342
+ if (component.types.length > 1) {
343
+ typeDropdown = `
344
+ <div class="page-type-dropdown">
345
+ <p>Type: </p>
346
+ <select class="type-dropdown" onchange="window.location.href=this.value">
347
+ ${sortedTypes.map(typeObj => `<option value="${typeObj.url}" data-support="${typeObj.support}">${typeObj.type.charAt(0).toUpperCase() + typeObj.type.slice(1)}</option>`).join('')}
348
+ </select>
349
+ </div>
350
+ <script>
351
+ // Initialize Choices.js for type dropdowns
352
+ document.addEventListener('DOMContentLoaded', function() {
353
+ const typeDropdowns = document.querySelectorAll('.type-dropdown');
354
+ typeDropdowns.forEach(dropdown => {
355
+ new Choices(dropdown, { searchEnabled: false, allowHTML: true, shouldSort: false });
356
+ });
357
+ });
358
+ </script>`;
359
+ }
360
+ return self.createBlock(parent, 'pass', typeDropdown + enterpriseAdmonition);
246
361
  });
247
362
  });
248
363
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redpanda-data/docs-extensions-and-macros",
3
- "version": "3.3.1",
3
+ "version": "3.3.3",
4
4
  "description": "Antora extensions and macros developed for Redpanda documentation.",
5
5
  "keywords": [
6
6
  "antora",