@internetstiftelsen/styleguide 5.1.15 → 5.1.16

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.
@@ -76,120 +76,353 @@ function _interop_require_wildcard(obj, nodeInterop) {
76
76
  _aggridcommunity.ModuleRegistry.registerModules([
77
77
  _aggridcommunity.AllCommunityModule
78
78
  ]);
79
- // to use iisTheme in an application, pass it to the theme grid option
79
+ // ---- THEME -----------------------------------------------------------
80
80
  const iisTheme = _aggridcommunity.themeQuartz.withPart(_aggridcommunity.iconSetMaterial).withParams({
81
- accentColor: "#0477CE",
82
- backgroundColor: "#FFFFFF",
81
+ accentColor: '#0477CE',
82
+ backgroundColor: '#FFFFFF',
83
83
  borderRadius: 3,
84
- browserColorScheme: "light",
85
- cellTextColor: "#1F2A36",
84
+ browserColorScheme: 'light',
85
+ cellTextColor: '#1F2A36',
86
86
  chromeBackgroundColor: {
87
- ref: "foregroundColor",
87
+ ref: 'foregroundColor',
88
88
  mix: 0.07,
89
- onto: "backgroundColor"
89
+ onto: 'backgroundColor'
90
90
  },
91
- fontFamily: "inherit",
91
+ fontFamily: 'inherit',
92
92
  fontSize: 16,
93
- foregroundColor: "#1F2A36",
94
- headerBackgroundColor: "#D8D8D8",
95
- headerFontFamily: "inherit",
93
+ foregroundColor: '#1F2A36',
94
+ headerBackgroundColor: '#D8D8D8',
95
+ headerFontFamily: 'inherit',
96
96
  headerFontSize: 16,
97
97
  headerFontWeight: 400,
98
- headerTextColor: "#1F2A36",
99
- oddRowBackgroundColor: "#EDEDED",
98
+ headerTextColor: '#1F2A36',
99
+ oddRowBackgroundColor: '#EDEDED',
100
100
  wrapperBorderRadius: 3
101
101
  });
102
- let gridApi;
103
- const gridOptions = {
104
- theme: iisTheme,
105
- sortable: true,
106
- suppressAutoHideAllSortIcons: true,
107
- columnDefs: [
108
- {
109
- headerName: 'Domain',
110
- field: 'domain',
111
- sortable: true,
112
- filter: true
102
+ // ---- RENDERERS -------------------------------------------------------
103
+ const renderStatusIcon = (params)=>{
104
+ const status = params.value;
105
+ if (!status) return '';
106
+ const map = {
107
+ passed: {
108
+ icon: '#icon-security-variant',
109
+ color: '#25c279',
110
+ label: 'Passed'
113
111
  },
114
- {
115
- headerName: 'Score (%)',
116
- field: 'score_percent',
117
- sortable: true,
118
- filter: 'agNumberColumnFilter',
119
- width: 120
112
+ failed: {
113
+ icon: '#icon-unsecure-variant',
114
+ color: '#d9002f',
115
+ label: 'Failed'
120
116
  },
121
- {
122
- headerName: 'Report',
123
- field: 'report_url',
124
- cellRenderer: (params)=>{
125
- return params.value ? `<a href="${params.value}" target="_blank" rel="noopener">View</a>` : '';
126
- }
117
+ warning: {
118
+ icon: '#icon-warning-variant',
119
+ color: '#f99963',
120
+ label: 'Warning'
121
+ },
122
+ error: {
123
+ icon: '#icon-unsecure-variant',
124
+ color: '#8E9297',
125
+ label: 'Error'
126
+ },
127
+ not_tested: {
128
+ icon: '#icon-unsecure-variant',
129
+ color: '#d8d8d8',
130
+ label: 'Not tested'
127
131
  },
132
+ informational: {
133
+ icon: '#icon-info-variant',
134
+ color: '#50b2fc',
135
+ label: 'Informational'
136
+ }
137
+ };
138
+ const { icon, color, label } = map[status] || {};
139
+ return icon ? `<span class="cell-center">
140
+ <svg class="status-icon" fill="${color}" width="20" height="20" aria-label="${label}">
141
+ <use xlink:href="${icon}"></use>
142
+ </svg>
143
+ <span class="status-text">${label}</span>
144
+ </span>` : status;
145
+ };
146
+ // ---- HELPERS ---------------------------------------------------------
147
+ const titleCase = (s)=>s.replace(/[_\-\.]+/g, ' ').replace(/\b\w/g, (c)=>c.toUpperCase());
148
+ function buildColumnDefsFromData(data) {
149
+ if (!Array.isArray(data) || data.length === 0) return [
128
150
  {
151
+ headerName: 'No Data',
152
+ field: 'noData'
153
+ }
154
+ ];
155
+ const sample = data[0];
156
+ // Internet.nl-like shape
157
+ if (sample && sample.results && typeof sample.results === 'object') {
158
+ const base = [
159
+ {
160
+ headerName: 'Domain',
161
+ field: 'domain',
162
+ minWidth: 200
163
+ },
164
+ {
165
+ headerName: 'Score (%)',
166
+ field: 'score_percent',
167
+ filter: 'agNumberColumnFilter',
168
+ minWidth: 140
169
+ },
170
+ {
171
+ headerName: 'Report',
172
+ field: 'report_url',
173
+ cellRenderer: (p)=>p.value ? `<a href="${p.value}" target="_blank" rel="noopener">View</a>` : '',
174
+ maxWidth: 220,
175
+ flex: 0
176
+ }
177
+ ];
178
+ const resultKeys = Object.keys(sample.results);
179
+ const childColIds = [];
180
+ const children = resultKeys.map((k, idx)=>{
181
+ const colId = `results.${k}.status`;
182
+ childColIds.push(colId);
183
+ return {
184
+ colId,
185
+ headerName: titleCase(k),
186
+ field: colId,
187
+ cellRenderer: renderStatusIcon,
188
+ tooltipValueGetter: (p)=>{
189
+ var _p_data_results_k, _p_data_results, _p_data;
190
+ const since = p == null ? void 0 : (_p_data = p.data) == null ? void 0 : (_p_data_results = _p_data.results) == null ? void 0 : (_p_data_results_k = _p_data_results[k]) == null ? void 0 : _p_data_results_k.since;
191
+ return since ? `Since: ${since}` : '';
192
+ },
193
+ cellClass: (p)=>`status-${p.value}`,
194
+ minWidth: 150,
195
+ sortable: true,
196
+ filter: true,
197
+ hide: idx > 0
198
+ };
199
+ });
200
+ base.push({
129
201
  headerName: 'Results',
130
- children: [
202
+ headerGroupComponent: 'ExpandHeader',
203
+ headerGroupComponentParams: {
204
+ childColIds,
205
+ startExpanded: false
206
+ },
207
+ children
208
+ });
209
+ return base;
210
+ }
211
+ // Generic fallback
212
+ const cols = [];
213
+ for (const [key, val] of Object.entries(sample)){
214
+ if (val && typeof val === 'object' && !Array.isArray(val)) {
215
+ for (const subKey of Object.keys(val)){
216
+ if (val[subKey] !== null && typeof val[subKey] !== 'object') {
217
+ cols.push({
218
+ headerName: `${titleCase(key)} • ${titleCase(subKey)}`,
219
+ field: `${key}.${subKey}`,
220
+ sortable: true,
221
+ filter: true,
222
+ minWidth: 140
223
+ });
224
+ }
225
+ }
226
+ } else {
227
+ cols.push({
228
+ headerName: titleCase(key),
229
+ field: key,
230
+ sortable: true,
231
+ filter: true,
232
+ minWidth: 120
233
+ });
234
+ }
235
+ }
236
+ return cols;
237
+ }
238
+ // Version-safe column state helper
239
+ function setColumnsState(params, { state = [], defaultState = {
240
+ hide: false,
241
+ pinned: null
242
+ } }) {
243
+ const columnApi = params.columnApi || (params.api && typeof params.api.getColumnApi === 'function' ? params.api.getColumnApi() : null);
244
+ if (columnApi && typeof columnApi.applyColumnState === 'function') {
245
+ columnApi.applyColumnState({
246
+ state,
247
+ defaultState
248
+ });
249
+ return;
250
+ }
251
+ if (params.api && typeof params.api.applyColumnState === 'function') {
252
+ params.api.applyColumnState({
253
+ state,
254
+ defaultState
255
+ });
256
+ return;
257
+ }
258
+ if (columnApi && typeof columnApi.setColumnState === 'function') {
259
+ if (typeof columnApi.resetColumnState === 'function') columnApi.resetColumnState();
260
+ columnApi.setColumnState(state);
261
+ return;
262
+ }
263
+ console.warn('No applicable column state API found in this AG Grid version.');
264
+ }
265
+ // Per-grid responsive visibility
266
+ function applyResponsiveVisibility(params, el) {
267
+ const w = (el == null ? void 0 : el.getBoundingClientRect().width) || (el == null ? void 0 : el.clientWidth) || 0;
268
+ if (w < 520) {
269
+ setColumnsState(params, {
270
+ state: [
131
271
  {
132
- headerName: 'IPv6',
133
- field: 'results.ipv6.status',
134
- sortable: true,
135
- filter: true,
136
- cellClass: (params)=>`status-${params.value}`
272
+ colId: 'domain',
273
+ pinned: 'left'
137
274
  },
138
275
  {
139
- headerName: 'DNSSEC',
140
- field: 'results.dnssec.status',
141
- sortable: true,
142
- filter: true,
143
- cellClass: (params)=>`status-${params.value}`
276
+ colId: 'results.rpki.status',
277
+ hide: true
144
278
  },
145
279
  {
146
- headerName: 'HTTPS',
147
- field: 'results.https.status',
148
- sortable: true,
149
- filter: true,
150
- cellClass: (params)=>`status-${params.value}`
280
+ colId: 'results.security_options.status',
281
+ hide: true
151
282
  },
152
283
  {
153
- headerName: 'Security Options',
154
- field: 'results.security_options.status',
155
- sortable: true,
156
- filter: true,
157
- cellClass: (params)=>`status-${params.value}`
284
+ colId: 'results.ipv6.status',
285
+ hide: true
286
+ }
287
+ ]
288
+ });
289
+ } else if (w < 760) {
290
+ setColumnsState(params, {
291
+ state: [
292
+ {
293
+ colId: 'domain',
294
+ pinned: null
158
295
  },
159
296
  {
160
- headerName: 'RPKI',
161
- field: 'results.rpki.status',
162
- sortable: true,
163
- filter: true,
164
- cellClass: (params)=>`status-${params.value}`
297
+ colId: 'results.rpki.status',
298
+ hide: true
165
299
  }
166
300
  ]
167
- }
168
- ]
169
- };
170
- (function fillLarge() {
171
- setWidthAndHeight("100%");
172
- })();
173
- function setWidthAndHeight(size) {
174
- const eGridDiv = document.querySelector("#myGrid");
175
- eGridDiv.style.setProperty("width", size);
176
- eGridDiv.style.setProperty("height", size);
301
+ });
302
+ } else {
303
+ setColumnsState(params, {
304
+ state: []
305
+ });
306
+ }
177
307
  }
178
- const gridDiv = document.querySelector("#myGrid");
179
- gridApi = _aggridcommunity.createGrid(gridDiv, gridOptions);
180
- fetch('public/assets/json/table.json').then((res)=>_async_to_generator(function*() {
181
- const ct = res.headers.get('content-type') || '';
182
- if (!res.ok) {
183
- const body = yield res.text();
184
- throw new Error(`Fetch failed ${res.status} ${res.statusText}\n${body.slice(0, 200)}`);
185
- }
186
- if (!ct.includes('application/json')) {
187
- const body = yield res.text();
188
- throw new Error(`Expected JSON, got ${ct}. First 200 chars:\n${body.slice(0, 200)}`);
308
+ // ---- EXPAND/COLLAPSE GROUP HEADER -----------------------------------
309
+ class ExpandHeader {
310
+ init(params) {
311
+ this.params = params;
312
+ this.childColIds = params.childColIds || [];
313
+ this.expanded = !!params.startExpanded;
314
+ // Debug: prove we mounted
315
+ console.log('[ExpandHeader] init for group:', params.displayName, 'children:', this.childColIds);
316
+ // Create a full-width, clickable label (no AG classes needed)
317
+ const e = document.createElement('div');
318
+ e.className = 'expand-header';
319
+ e.style.cssText = 'display:flex;align-items:center;gap:6px;cursor:pointer;width:100%;';
320
+ var _params_displayName;
321
+ e.innerHTML = `
322
+ <span class="expander" style="display:inline-block;transition:transform .15s">▶</span>
323
+ <span class="title">${(_params_displayName = params.displayName) != null ? _params_displayName : 'Group'}</span>
324
+ `;
325
+ e.addEventListener('click', ()=>{
326
+ this.expanded = !this.expanded;
327
+ this.updateIcon();
328
+ toggleChildColumnsVisibility(params, this.childColIds, this.expanded);
329
+ });
330
+ this.eGui = e;
331
+ this.updateIcon();
332
+ // Apply initial expanded state
333
+ if (this.expanded) {
334
+ toggleChildColumnsVisibility(params, this.childColIds, true);
189
335
  }
190
- return res.json();
191
- })()).then((data)=>{
192
- console.log('Loaded JSON rows:', Array.isArray(data) ? data.length : data);
193
- }).catch((e)=>{
194
- console.error(e);
336
+ }
337
+ updateIcon() {
338
+ const icon = this.eGui.querySelector('.expander');
339
+ if (icon) icon.style.transform = this.expanded ? 'rotate(90deg)' : 'rotate(0deg)';
340
+ }
341
+ getGui() {
342
+ return this.eGui;
343
+ }
344
+ refresh() {
345
+ return true;
346
+ }
347
+ }
348
+ function toggleChildColumnsVisibility(params, colIds, makeVisible) {
349
+ var _params_api_sizeColumnsToFit, _params_api;
350
+ const columnApi = params.columnApi || (params.api && typeof params.api.getColumnApi === 'function' ? params.api.getColumnApi() : null);
351
+ if (!columnApi) return;
352
+ columnApi.setColumnsVisible(colIds, !!makeVisible);
353
+ (_params_api = params.api) == null ? void 0 : (_params_api_sizeColumnsToFit = _params_api.sizeColumnsToFit) == null ? void 0 : _params_api_sizeColumnsToFit.call(_params_api);
354
+ }
355
+ // Factory to create per-grid options with the correct closures
356
+ function makeGridOptionsFor(el) {
357
+ return {
358
+ theme: iisTheme,
359
+ components: {
360
+ ExpandHeader
361
+ },
362
+ defaultColDef: {
363
+ resizable: true,
364
+ sortable: true,
365
+ filter: true,
366
+ flex: 1,
367
+ minWidth: 150,
368
+ unSortIcon: true
369
+ },
370
+ columnDefs: [],
371
+ rowData: [],
372
+ onGridReady (params) {
373
+ return _async_to_generator(function*() {
374
+ try {
375
+ // dynamic JSON import based on *this* element's data attribute
376
+ const attr = el.dataset.json || './table.json';
377
+ const jsonUrl = new URL(attr, require("url").pathToFileURL(__filename).toString()).toString();
378
+ const mod = yield Promise.resolve(/* @vite-ignore */ jsonUrl).then((p)=>/*#__PURE__*/ _interop_require_wildcard(require(p)));
379
+ var _mod_default;
380
+ const data = (_mod_default = mod == null ? void 0 : mod.default) != null ? _mod_default : mod;
381
+ const cols = buildColumnDefsFromData(data);
382
+ params.api.setGridOption('columnDefs', cols);
383
+ params.api.setGridOption('rowData', data);
384
+ applyResponsiveVisibility(params, el);
385
+ params.api.sizeColumnsToFit();
386
+ } catch (e) {
387
+ console.error('Dynamic JSON import failed for grid:', el, e);
388
+ }
389
+ })();
390
+ },
391
+ onGridSizeChanged (params) {
392
+ applyResponsiveVisibility(params, el);
393
+ params.api.sizeColumnsToFit();
394
+ },
395
+ animateRows: true
396
+ };
397
+ }
398
+ // ---- INIT ALL GRIDS --------------------------------------------------
399
+ document.addEventListener('DOMContentLoaded', ()=>{
400
+ // Use a class selector so you can have multiple grids;
401
+ // make sure your HTML uses: <div class="ag-theme-quartz js-ag-grid" data-json="./table1.json"></div>
402
+ const containers = document.querySelectorAll('.js-ag-grid');
403
+ containers.forEach((el)=>{
404
+ // Ensure theme class + measurable size BEFORE createGrid (each grid separately)
405
+ if (!el.classList.contains('ag-theme-quartz')) el.classList.add('ag-theme-quartz');
406
+ //if (!el.style.height) el.style.height = '600px';
407
+ if (!el.style.width) el.style.width = '100%';
408
+ const gridOptions = makeGridOptionsFor(el);
409
+ const api = _aggridcommunity.createGrid(el, gridOptions);
410
+ // Per-grid ResizeObserver (don’t reuse one global API)
411
+ const ro = new ResizeObserver(()=>{
412
+ var _gridOptions_api, _gridOptions_api1;
413
+ (_gridOptions_api = gridOptions.api) == null ? void 0 : _gridOptions_api.onGridSizeChanged();
414
+ (_gridOptions_api1 = gridOptions.api) == null ? void 0 : _gridOptions_api1.sizeColumnsToFit();
415
+ });
416
+ ro.observe(el);
417
+ });
418
+ });
419
+ // ---- TOGGLE FULLSCREEN CLASS ON PARENT CONTAINER WHEN CLICKING FULLSCREEN BUTTON ----
420
+ document.querySelectorAll('[data-ag-grid-fullscreen]').forEach((btn)=>{
421
+ btn.addEventListener('click', (e)=>{
422
+ // Find the nearest parent container for this button
423
+ const gridEl = btn.closest('.js-ag-grid');
424
+ if (!gridEl) return;
425
+ // Toggle fullscreen class only for this specific element
426
+ gridEl.classList.toggle('has-fullscreen');
427
+ });
195
428
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@internetstiftelsen/styleguide",
3
- "version": "5.1.15",
3
+ "version": "5.1.16",
4
4
  "main": "dist/components.js",
5
5
  "ports": {
6
6
  "fractal": "3000"