Skip to content

Commit 373cf06

Browse files
authored
Merge pull request #29 from SolverForge/issue-10-api-knobs
Complete remaining button and rail config knobs
2 parents e6e3c0f + 6ea5f7b commit 373cf06

6 files changed

Lines changed: 124 additions & 2 deletions

File tree

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ SF.createButton({ text: 'Stop', variant: 'danger' }) // red bg, white tex
157157
SF.createButton({ text: 'Save', variant: 'primary' }) // emerald-700 bg
158158
SF.createButton({ text: 'Cancel', variant: 'default' }) // gray border
159159
SF.createButton({ icon: 'fa-gear', variant: 'ghost', circle: true })
160+
SF.createButton({ text: 'Settings', icon: 'fa-gear', variant: 'ghost', iconOnly: true })
160161
SF.createButton({ text: 'Submit', variant: 'primary', pill: true })
161162
SF.createButton({ text: 'Delete', variant: 'danger', outline: true })
162163
SF.createButton({ text: 'Sm', variant: 'primary', size: 'small' })
@@ -184,6 +185,10 @@ var card = SF.rail.createCard({
184185
columns: 5,
185186
type: 'CAMERA',
186187
typeStyle: { bg: 'rgba(59,130,246,0.15)', color: '#1d4ed8', border: '1px solid rgba(59,130,246,0.3)' },
188+
badges: [
189+
'TEMPRA',
190+
{ label: 'HOT', style: { bg: 'rgba(239,68,68,0.12)', color: '#b91c1c', border: '1px solid rgba(239,68,68,0.3)' } },
191+
],
187192
gauges: [
188193
{ label: 'Temp', pct: 85, style: 'heat', text: '850/1000°C' },
189194
{ label: 'Load', pct: 60, style: 'load', text: '120/200 kg' },
@@ -216,6 +221,7 @@ card.setSolving(true);
216221
```
217222

218223
Gauge styles: `heat` (blue→amber→red), `load` (emerald→amber→red), `emerald` (solid green).
224+
`badges` accepts either strings or `{ label, style }` objects for extra resource metadata.
219225

220226
## Gantt Chart
221227

WIREFRAME.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,7 @@ var header = SF.rail.createHeader({
312312
var card = SF.rail.createCard({
313313
id: 'furnace-1', name: 'FORNO 1', labelWidth: 200,
314314
type: 'CAMERA', typeStyle: { bg: 'rgba(59,130,246,0.15)', color: '#1d4ed8' },
315+
badges: ['TEMPRA', { label: 'HOT', style: { bg: 'rgba(239,68,68,0.12)', color: '#b91c1c' } }],
315316
columns: 5,
316317
gauges: [
317318
{ label: 'Temp', pct: 85, style: 'heat', text: '850/1000°C' },

js-src/03-buttons.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
btn.appendChild(icon);
3333
}
3434

35-
if (config.text && !config.circle) {
35+
if (config.text && !config.circle && !config.iconOnly) {
3636
btn.appendChild(document.createTextNode(config.text));
3737
}
3838

@@ -46,6 +46,8 @@
4646

4747
if (config.ariaLabel) {
4848
btn.setAttribute('aria-label', config.ariaLabel);
49+
} else if (config.iconOnly && config.text) {
50+
btn.setAttribute('aria-label', config.text);
4951
} else if (config.icon && !config.text) {
5052
btn.setAttribute('aria-label', config.icon.replace(/fa-/, '').replace(/-/g, ' '));
5153
}

js-src/13-rail.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,27 @@
6565
}
6666
meta.appendChild(badge);
6767
}
68+
var badges = Array.isArray(config.badges)
69+
? config.badges
70+
: config.badges
71+
? [config.badges]
72+
: [];
73+
if (badges.length) {
74+
badges.forEach(function (entry) {
75+
if (!entry) return;
76+
if (typeof entry === 'string') {
77+
meta.appendChild(sf.el('span', { className: 'sf-resource-type-badge' }, entry));
78+
return;
79+
}
80+
var extraBadge = sf.el('span', { className: 'sf-resource-type-badge' }, entry.label || '');
81+
if (entry.style) {
82+
extraBadge.style.background = entry.style.bg || '';
83+
extraBadge.style.color = entry.style.color || '';
84+
extraBadge.style.border = entry.style.border || '';
85+
}
86+
meta.appendChild(extraBadge);
87+
});
88+
}
6889
identity.appendChild(meta);
6990
}
7091
resHeader.appendChild(identity);
@@ -130,6 +151,7 @@
130151
horizon: config.heatmap.horizon || 1,
131152
label: config.heatmap.label,
132153
segments: config.heatmap.segments,
154+
labelWidth: labelWidth,
133155
};
134156
heatmapCfg.railConfig = config;
135157
var heatmap = sf.rail.createHeatmap(heatmapCfg);
@@ -185,6 +207,7 @@
185207
if (!config || !config.segments || !Array.isArray(config.segments) || config.segments.length === 0) return null;
186208

187209
var heatmap = sf.el('div', { className: 'sf-heatmap' });
210+
heatmap.style.gridTemplateColumns = (config.labelWidth || 200) + 'px 1fr';
188211
var label = sf.el('div', { className: 'sf-heatmap-label' }, config.label || '');
189212
heatmap.appendChild(label);
190213

static/sf/sf.js

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ const SF = (function () {
215215
btn.appendChild(icon);
216216
}
217217

218-
if (config.text && !config.circle) {
218+
if (config.text && !config.circle && !config.iconOnly) {
219219
btn.appendChild(document.createTextNode(config.text));
220220
}
221221

@@ -229,6 +229,8 @@ const SF = (function () {
229229

230230
if (config.ariaLabel) {
231231
btn.setAttribute('aria-label', config.ariaLabel);
232+
} else if (config.iconOnly && config.text) {
233+
btn.setAttribute('aria-label', config.text);
232234
} else if (config.icon && !config.text) {
233235
btn.setAttribute('aria-label', config.icon.replace(/fa-/, '').replace(/-/g, ' '));
234236
}
@@ -1220,6 +1222,27 @@ const SF = (function () {
12201222
}
12211223
meta.appendChild(badge);
12221224
}
1225+
var badges = Array.isArray(config.badges)
1226+
? config.badges
1227+
: config.badges
1228+
? [config.badges]
1229+
: [];
1230+
if (badges.length) {
1231+
badges.forEach(function (entry) {
1232+
if (!entry) return;
1233+
if (typeof entry === 'string') {
1234+
meta.appendChild(sf.el('span', { className: 'sf-resource-type-badge' }, entry));
1235+
return;
1236+
}
1237+
var extraBadge = sf.el('span', { className: 'sf-resource-type-badge' }, entry.label || '');
1238+
if (entry.style) {
1239+
extraBadge.style.background = entry.style.bg || '';
1240+
extraBadge.style.color = entry.style.color || '';
1241+
extraBadge.style.border = entry.style.border || '';
1242+
}
1243+
meta.appendChild(extraBadge);
1244+
});
1245+
}
12231246
identity.appendChild(meta);
12241247
}
12251248
resHeader.appendChild(identity);
@@ -1285,6 +1308,7 @@ const SF = (function () {
12851308
horizon: config.heatmap.horizon || 1,
12861309
label: config.heatmap.label,
12871310
segments: config.heatmap.segments,
1311+
labelWidth: labelWidth,
12881312
};
12891313
heatmapCfg.railConfig = config;
12901314
var heatmap = sf.rail.createHeatmap(heatmapCfg);
@@ -1340,6 +1364,7 @@ const SF = (function () {
13401364
if (!config || !config.segments || !Array.isArray(config.segments) || config.segments.length === 0) return null;
13411365

13421366
var heatmap = sf.el('div', { className: 'sf-heatmap' });
1367+
heatmap.style.gridTemplateColumns = (config.labelWidth || 200) + 'px 1fr';
13431368
var label = sf.el('div', { className: 'sf-heatmap-label' }, config.label || '');
13441369
heatmap.appendChild(label);
13451370

tests/button-rail-knobs.test.js

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
const assert = require('node:assert/strict');
2+
const fs = require('node:fs');
3+
const path = require('node:path');
4+
const test = require('node:test');
5+
const vm = require('node:vm');
6+
7+
const { createDom } = require('./support/fake-dom');
8+
9+
const ROOT = path.resolve(__dirname, '..');
10+
11+
function loadSf(files, overrides = {}) {
12+
const { document, window, Node } = createDom();
13+
const context = vm.createContext({
14+
console,
15+
document,
16+
window,
17+
Node,
18+
setTimeout,
19+
clearTimeout,
20+
...overrides,
21+
});
22+
23+
files.forEach((file) => {
24+
const source = fs.readFileSync(path.join(ROOT, file), 'utf8');
25+
vm.runInContext(source, context, { filename: file });
26+
});
27+
28+
return { SF: context.window.SF, document };
29+
}
30+
31+
test('iconOnly buttons keep an accessible label without rendering text content', () => {
32+
const { SF } = loadSf(['js-src/00-core.js', 'js-src/03-buttons.js']);
33+
34+
const button = SF.createButton({
35+
text: 'Settings',
36+
icon: 'fa-gear',
37+
iconOnly: true,
38+
});
39+
40+
assert.equal(button.textContent, '');
41+
assert.equal(button.attributes['aria-label'], 'Settings');
42+
});
43+
44+
test('rail card badges accept a single string badge and preserve heatmap alignment', () => {
45+
const { SF } = loadSf(['js-src/00-core.js', 'js-src/13-rail.js']);
46+
47+
const card = SF.rail.createCard({
48+
name: 'Kiln 1',
49+
badges: 'TEMPRA',
50+
labelWidth: 220,
51+
columns: 4,
52+
heatmap: {
53+
label: 'Load',
54+
horizon: 100,
55+
segments: [{ start: 0, end: 25, color: '#0f0' }],
56+
},
57+
});
58+
59+
const badges = card.el.querySelectorAll('.sf-resource-type-badge');
60+
const heatmap = card.el.querySelector('.sf-heatmap');
61+
62+
assert.equal(badges.length, 1);
63+
assert.equal(badges[0].textContent, 'TEMPRA');
64+
assert.equal(heatmap.style.gridTemplateColumns, '220px 1fr');
65+
});

0 commit comments

Comments
 (0)