Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:

strategy:
matrix:
node-version: [18.x, 20.x, 22.x]
node-version: [20.x, 22.x, 24.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/

steps:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ test/fixtures/components/lodash-component/_package
test/fixtures/components/no-containers/_package
test/fixtures/components/welcome/_package
test/fixtures/components/welcome-with-optional-parameters/_package
test/fixtures/components/hello-world-custom-cookies/_package

logintervals.md
dist/
Expand Down
12 changes: 10 additions & 2 deletions src/registry/routes/component-info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,17 @@ function getParams(component: Component) {
params = Object.fromEntries(
Object.entries(component.oc.parameters || {})
.filter(([, param]) => {
return !!param.mandatory && 'example' in param;
// Include parameters that have either a default value or an example
return param.default !== undefined || param.example !== undefined;
})
.map(([paramName, param]) => {
// Prefer default value over example
const value =
param.default !== undefined
? String(param.default)
: param.example!;
return [paramName, value];
})
.map(([paramName, param]) => [paramName, param.example!])
);
}

Expand Down
21 changes: 18 additions & 3 deletions src/registry/views/info.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ export default function Info(vm: Vm) {
selectedVersion={component.version}
/>
</h2>
<p class="w-100">
<p class="w-100" safe>
{component.description} {componentState()}
</p>
{statsAvailable ? (
Expand Down Expand Up @@ -184,15 +184,30 @@ export default function Info(vm: Vm) {
) : (
''
)}
<ComponentParameters parameters={component.oc.parameters} />
<ComponentParameters
parameters={component.oc.parameters}
currentValues={Object.fromEntries(
Object.entries(component.oc.parameters || {})
.filter(([, param]) => {
return param.default !== undefined || param.example !== undefined;
})
.map(([paramName, param]) => {
const value =
param.default !== undefined
? String(param.default)
: param.example!;
return [paramName, value];
})
)}
/>
<h3>Code</h3>
<p>
You can edit the following area and then
<a href="#refresh" class="refresh-preview">
{' '}
refresh{' '}
</a>
to apply the change into the preview window.
or hit Enter to apply the change into the preview window.
</p>
<div class="field">
<p>Component's href:</p>
Expand Down
6 changes: 3 additions & 3 deletions src/registry/views/partials/component-author.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ const componentAuthor = (props: {
<div class="field">
<p>Author:</p>
{!name && !email && !url && <span>not available</span>}
{name && <span>{name}&nbsp;</span>}
{!!name && <span safe>{name}&nbsp;</span>}
{email && (
<span>
<a href={`mailto:${email}`}>{email}</a>&nbsp;
</span>
)}
{url && (
{!!url && (
<span>
<a href={url} target="_blank" rel="noreferrer">
<a safe href={url} target="_blank" rel="noreferrer">
{url}
</a>
</span>
Expand Down
84 changes: 72 additions & 12 deletions src/registry/views/partials/component-parameters.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import type { OcParameter } from '../../../types';

const componentParameters = ({
parameters
parameters,
currentValues
}: {
parameters?: Record<string, OcParameter>;
currentValues?: Record<string, string>;
}) => {
if (!parameters) {
return (
Expand All @@ -16,32 +18,94 @@ const componentParameters = ({

const parameterRow = (param: OcParameter, paramName: string) => {
const mandatory = param.mandatory ? 'mandatory' : 'optional';
const defaultValue =
param.default !== undefined ? String(param.default) : '';
const exampleValue =
param.example !== undefined ? String(param.example) : '';
const currentValue =
currentValues?.[paramName] || defaultValue || exampleValue;

const renderInput = () => {
switch (param.type) {
case 'number':
return (
<input
type="number"
class="parameter-input"
name={paramName}
data-parameter={paramName}
value={currentValue}
placeholder={param.example || ''}
/>
);
case 'boolean': {
const isChecked = String(currentValue) === 'true';
return (
<input
type="checkbox"
class="parameter-input"
name={paramName}
data-parameter={paramName}
checked={isChecked}
/>
);
}
default:
return (
<input
type="text"
class="parameter-input"
name={paramName}
data-parameter={paramName}
value={currentValue}
placeholder={
typeof param.default !== 'undefined'
? String(param.default)
: param.example || ''
}
/>
);
}
};

return (
<div class="row">
<div class="parameter">
<span class="bold">{paramName}</span>
<span safe class="bold">
{paramName}
</span>
<span>
({param.type}, {mandatory})
</span>
</div>
<div class="parameter-description">
{param.description && (
{!!param.description && (
<>
<span>{param.description}</span>
<span safe>{param.description}</span>
<br />
<br />
</>
)}
<span class="bold">Example:</span>
<span>{param.example}</span>
{!param.mandatory && param.default && (
{param.type !== 'boolean' && !!param.example && (
<>
<span class="bold">Example:</span>
<span>{param.example}</span>
<br />
<br />
</>
)}
{!param.mandatory && !!param.default && (
<>
<span class="bold">Default:</span>
<span>{param.default}</span>
<span safe>{String(param.default)}</span>
<br />
<br />
</>
)}
<span class="bold">
{param.type === 'boolean' ? 'Enabled:' : 'Value:'}
</span>
{renderInput()}
</div>
</div>
);
Expand All @@ -51,10 +115,6 @@ const componentParameters = ({
<div>
<h3>Parameters</h3>
<div id="plugins" class="table">
<div class="row header">
<div class="parameter">Parameters</div>
<div class="parameter-description" />
</div>
{Object.keys(parameters).map((parameterName) =>
parameterRow(parameters[parameterName], parameterName)
)}
Expand Down
2 changes: 1 addition & 1 deletion src/registry/views/partials/component-versions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const componentVersions = ({
return (
<select id="versions">
{versions.map((version) => (
<option value={version} selected={version === selectedVersion}>
<option safe value={version} selected={version === selectedVersion}>
{version}
</option>
))}
Expand Down
4 changes: 3 additions & 1 deletion src/registry/views/partials/components-dependencies.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ const ComponentsDependencies = (props: {
return (
<a href={link} target="_blank" rel="noreferrer">
<div class="componentRow row table">
<p class="release">{label}</p>
<p safe class="release">
{label}
</p>
</div>
</a>
);
Expand Down
18 changes: 13 additions & 5 deletions src/registry/views/partials/components-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ const ComponentsList = (props: {

const remoteServerColumns = isRemote
? [
<div class="date">{component.oc.stringifiedDate || ''}</div>,
<div class="date" safe>
{component.oc.stringifiedDate || ''}
</div>,
<div class="activity">{component.allVersions.length}</div>
]
: null;
Expand All @@ -49,12 +51,18 @@ const ComponentsList = (props: {
class={`componentRow row table${isHidden ? ' hide' : ''}`}
>
<div class="title">
<p class="name">{component.name}</p>
<span class="description">{component.description}</span>
<p class="name" safe>
{component.name}
</p>
<span class="description" safe>
{component.description}
</span>
</div>
{componentState}
<div class="author">{component.author.name || ''}</div>
<div>{component.version}</div>
<div class="author" safe>
{component.author.name || ''}
</div>
<div safe>{component.version}</div>
{remoteServerColumns}
{sizeColumn}
</div>
Expand Down
3 changes: 2 additions & 1 deletion src/registry/views/partials/components-templates.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const ComponentsTemplates = (props: { templates: Templates }) => {
global: string | string[];
url: string;
}) => (
<a href={url} target="_blank" rel="noreferrer">
<a safe href={url} target="_blank" rel="noreferrer">
{global}
</a>
);
Expand All @@ -29,6 +29,7 @@ const ComponentsTemplates = (props: { templates: Templates }) => {
href={`https://www.npmjs.com/package/${type}`}
target="_blank"
rel="noreferrer"
safe
>
{type}@{version}
</a>
Expand Down
2 changes: 1 addition & 1 deletion src/registry/views/partials/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const Layout = ({ title, href, children, scripts }: LayoutProps) => {
{'<!DOCTYPE html>'}
<html lang="en">
<head>
<title>{title}</title>
<title safe>{title}</title>
<meta name="robots" content="index, follow" />
<meta name="language" content="EN" />
<meta name="distribution" content="global" />
Expand Down
8 changes: 5 additions & 3 deletions src/registry/views/partials/property.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@ const Property = (props: {
if (!props.value) return null;

const label = props.linked ? (
<a href={String(props.value)}>{String(props.value)}</a>
<a safe href={String(props.value)}>
{String(props.value)}
</a>
) : (
<span>{String(props.value)}</span>
<span safe>{String(props.value)}</span>
);

return (
<div class="field">
<p>{props.display}:</p> {label}
<p safe>{props.display}:</p> {label}
</div>
);
};
Expand Down
54 changes: 54 additions & 0 deletions src/registry/views/static/info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,40 @@ oc.cmd.push(function() {

return false;
};

var updateHrefFromParameters = function() {
var baseUrl = $('#href').val().split('?')[0];
var params = {};

// Collect all parameter values
$('.parameter-input').each(function() {
var paramName = $(this).data('parameter');
var paramValue;

// Handle different input types
if ($(this).attr('type') === 'checkbox') {
paramValue = $(this).is(':checked') ? 'true' : 'false';
} else {
paramValue = $(this).val();
}

if (paramValue && paramValue.trim() !== '' && paramValue !== 'false') {
params[paramName] = paramValue.trim();
}
});

// Build query string
var queryString = Object.keys(params).map(function(key) {
return encodeURIComponent(key) + '=' + encodeURIComponent(params[key]);
}).join('&');

// Update href
var newHref = baseUrl + (queryString ? '?' + queryString : '');
$('#href').val(newHref);

return newHref;
};

$('.refresh-preview').click(refreshPreview);

// Add Enter key handler for the href input field
Expand All @@ -39,6 +73,26 @@ oc.cmd.push(function() {
}
});

// Handle parameter input changes
$('.parameter-input').on('input blur change', function() {
updateHrefFromParameters();
});

// Handle Enter key on parameter inputs (for text and number inputs)
$('.parameter-input[type="text"], .parameter-input[type="number"]').keypress(function(e) {
if (e.which === 13) { // Enter key
updateHrefFromParameters();
refreshPreview();
return false;
}
});

// Handle checkbox changes - immediately refresh preview
$('.parameter-input[type="checkbox"]').on('change', function() {
updateHrefFromParameters();
refreshPreview();
});

$('.open-preview').click(function() {
refreshPreview();
var url = $('.preview').attr('src');
Expand Down
Loading