@internetstiftelsen/styleguide 5.1.14 → 5.1.15-beta.0.1
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/dist/assets/js/youtube.js +3 -3
- package/dist/atoms/progress/progress.js +7 -3
- package/dist/atoms/textarea/rich-text.js +3 -3
- package/dist/components.js +1 -0
- package/dist/molecules/modal/modal.js +6 -6
- package/dist/molecules/table-advanced/table-advanced.js +195 -0
- package/package.json +2 -1
- package/src/app.scss +1 -0
- package/src/atoms/icon/_all-icons.zip +0 -0
- package/src/atoms/icon/exit-fullscreen.svg +1 -0
- package/src/atoms/icon/fullscreen.svg +1 -0
- package/src/atoms/progress/_progress.scss +30 -2
- package/src/atoms/progress/progress.config.js +2 -1
- package/src/atoms/progress/progress.js +9 -4
- package/src/components.js +1 -0
- package/src/configurations/icons.json +1 -1
- package/src/molecules/table/_table.scss +5 -1
- package/src/molecules/table-advanced/_table-advanced.scss +98 -0
- package/src/molecules/table-advanced/table-advanced.config.js +20 -0
- package/src/molecules/table-advanced/table-advanced.js +313 -0
- package/src/molecules/table-advanced/table-small.json +72 -0
- package/src/molecules/table-advanced/table.json +2027 -0
- package/src/molecules/table-advanced/test +246 -0
- package/src/organisms/selectable/_selectable.scss +62 -7
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
// table-advanced.js (AG Grid Community, vanilla JS)
|
|
2
|
+
|
|
3
|
+
import 'ag-grid-community/styles/ag-grid.css';
|
|
4
|
+
import 'ag-grid-community/styles/ag-theme-quartz.css';
|
|
5
|
+
|
|
6
|
+
import * as agGrid from "ag-grid-community";
|
|
7
|
+
import { themeQuartz, iconSetMaterial } from "ag-grid-community";
|
|
8
|
+
import { ModuleRegistry, AllCommunityModule } from "ag-grid-community";
|
|
9
|
+
|
|
10
|
+
ModuleRegistry.registerModules([AllCommunityModule]);
|
|
11
|
+
|
|
12
|
+
// THEME
|
|
13
|
+
const iisTheme = themeQuartz
|
|
14
|
+
.withPart(iconSetMaterial)
|
|
15
|
+
.withParams({
|
|
16
|
+
accentColor: "#0477CE",
|
|
17
|
+
backgroundColor: "#FFFFFF",
|
|
18
|
+
borderRadius: 3,
|
|
19
|
+
browserColorScheme: "light",
|
|
20
|
+
cellTextColor: "#1F2A36",
|
|
21
|
+
chromeBackgroundColor: { ref: "foregroundColor", mix: 0.07, onto: "backgroundColor" },
|
|
22
|
+
fontFamily: "inherit",
|
|
23
|
+
fontSize: 16,
|
|
24
|
+
foregroundColor: "#1F2A36",
|
|
25
|
+
headerBackgroundColor: "#D8D8D8",
|
|
26
|
+
headerFontFamily: "inherit",
|
|
27
|
+
headerFontSize: 16,
|
|
28
|
+
headerFontWeight: 400,
|
|
29
|
+
headerTextColor: "#1F2A36",
|
|
30
|
+
oddRowBackgroundColor: "#EDEDED",
|
|
31
|
+
wrapperBorderRadius: 3,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// RENDERERS
|
|
35
|
+
const renderStatusIcon = (params) => {
|
|
36
|
+
const status = params.value;
|
|
37
|
+
if (!status) return "";
|
|
38
|
+
|
|
39
|
+
const map = {
|
|
40
|
+
passed: { icon: "#icon-security-variant", color: "#25c279", label: "Passed" },
|
|
41
|
+
failed: { icon: "#icon-unsecure-variant", color: "#d9002f", label: "Failed" },
|
|
42
|
+
warning: { icon: "#icon-warning-variant", color: "#f99963", label: "Warning" },
|
|
43
|
+
error: { icon: "#icon-unsecure-variant", color: "#8E9297", label: "Error" },
|
|
44
|
+
not_tested: { icon: "#icon-unsecure-variant", color: "#d8d8d8", label: "Not tested" },
|
|
45
|
+
informational: { icon: "#icon-info-variant", color: "#50b2fc", label: "Informational" },
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const { icon, color, label } = map[status] || {};
|
|
49
|
+
return icon
|
|
50
|
+
? `<span class="cell-center">
|
|
51
|
+
<svg class="status-icon" fill="${color}" width="20" height="20" aria-label="${label}">
|
|
52
|
+
<use xlink:href="${icon}"></use>
|
|
53
|
+
</svg>
|
|
54
|
+
<span class="status-text">${label}</span>
|
|
55
|
+
</span>`
|
|
56
|
+
: status;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// UTIL: pretty header from key
|
|
60
|
+
const titleCase = (s) => s.replace(/[_\-\.]+/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
|
|
61
|
+
|
|
62
|
+
// Build columnDefs from data (handles your Internet.nl shape and a generic fallback)
|
|
63
|
+
function buildColumnDefsFromData(data) {
|
|
64
|
+
if (!Array.isArray(data) || data.length === 0) {
|
|
65
|
+
return [{ headerName: 'No Data', field: 'noData' }];
|
|
66
|
+
}
|
|
67
|
+
const sample = data[0];
|
|
68
|
+
|
|
69
|
+
// Special case: your dataset (domain/score/report + results.{key}.status / .since)
|
|
70
|
+
if (sample && typeof sample === 'object' && sample.results && typeof sample.results === 'object') {
|
|
71
|
+
const base = [
|
|
72
|
+
{ headerName: 'Domain', field: 'domain', minWidth: 200 },
|
|
73
|
+
{ headerName: 'Score (%)', field: 'score_percent', filter: 'agNumberColumnFilter', minWidth: 140 },
|
|
74
|
+
{
|
|
75
|
+
headerName: 'Report',
|
|
76
|
+
field: 'report_url',
|
|
77
|
+
cellRenderer: (p) => p.value ? `<a href="${p.value}" target="_blank" rel="noopener">View</a>` : '',
|
|
78
|
+
maxWidth: 220,
|
|
79
|
+
flex: 0
|
|
80
|
+
}
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
const resultKeys = Object.keys(sample.results);
|
|
84
|
+
const children = resultKeys.map((k) => ({
|
|
85
|
+
headerName: titleCase(k),
|
|
86
|
+
field: `results.${k}.status`,
|
|
87
|
+
cellRenderer: renderStatusIcon,
|
|
88
|
+
tooltipValueGetter: p => {
|
|
89
|
+
const since = p?.data?.results?.[k]?.since;
|
|
90
|
+
return since ? `Since: ${since}` : '';
|
|
91
|
+
},
|
|
92
|
+
cellClass: (p) => `status-${p.value}`,
|
|
93
|
+
minWidth: 150,
|
|
94
|
+
sortable: true,
|
|
95
|
+
filter: true,
|
|
96
|
+
}));
|
|
97
|
+
|
|
98
|
+
base.push({ headerName: 'Results', children });
|
|
99
|
+
return base;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Generic fallback: make columns for top-level primitives and one-level nested primitives
|
|
103
|
+
const cols = [];
|
|
104
|
+
for (const [key, val] of Object.entries(sample)) {
|
|
105
|
+
if (val && typeof val === 'object' && !Array.isArray(val)) {
|
|
106
|
+
for (const subKey of Object.keys(val)) {
|
|
107
|
+
if (val[subKey] !== null && typeof val[subKey] !== 'object') {
|
|
108
|
+
cols.push({
|
|
109
|
+
headerName: `${titleCase(key)} • ${titleCase(subKey)}`,
|
|
110
|
+
field: `${key}.${subKey}`,
|
|
111
|
+
sortable: true,
|
|
112
|
+
filter: true,
|
|
113
|
+
minWidth: 140
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
} else {
|
|
118
|
+
cols.push({
|
|
119
|
+
headerName: titleCase(key),
|
|
120
|
+
field: key,
|
|
121
|
+
sortable: true,
|
|
122
|
+
filter: true,
|
|
123
|
+
minWidth: 120
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return cols;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// VERSION-SAFE COLUMN STATE HELPER
|
|
131
|
+
function setColumnsState(params, { state = [], defaultState = { hide: false, pinned: null } }) {
|
|
132
|
+
const columnApi =
|
|
133
|
+
params.columnApi ||
|
|
134
|
+
(params.api && typeof params.api.getColumnApi === "function" ? params.api.getColumnApi() : null);
|
|
135
|
+
|
|
136
|
+
if (columnApi && typeof columnApi.applyColumnState === "function") {
|
|
137
|
+
columnApi.applyColumnState({ state, defaultState });
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (params.api && typeof params.api.applyColumnState === "function") {
|
|
141
|
+
params.api.applyColumnState({ state, defaultState });
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
if (columnApi && typeof columnApi.setColumnState === "function") {
|
|
145
|
+
if (typeof columnApi.resetColumnState === "function") columnApi.resetColumnState();
|
|
146
|
+
columnApi.setColumnState(state);
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
console.warn("No applicable column state API found in this AG Grid version.");
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Responsive visibility (no api.getGridSize)
|
|
153
|
+
function applyResponsiveVisibility(params, el) {
|
|
154
|
+
const w = el?.getBoundingClientRect().width || el?.clientWidth || 0;
|
|
155
|
+
|
|
156
|
+
if (w < 520) {
|
|
157
|
+
setColumnsState(params, {
|
|
158
|
+
state: [
|
|
159
|
+
{ colId: "domain", pinned: "left" },
|
|
160
|
+
{ colId: "results.rpki.status", hide: true },
|
|
161
|
+
{ colId: "results.security_options.status", hide: true },
|
|
162
|
+
{ colId: "results.ipv6.status", hide: true },
|
|
163
|
+
],
|
|
164
|
+
});
|
|
165
|
+
} else if (w < 760) {
|
|
166
|
+
setColumnsState(params, {
|
|
167
|
+
state: [
|
|
168
|
+
{ colId: "domain", pinned: null },
|
|
169
|
+
{ colId: "results.rpki.status", hide: true },
|
|
170
|
+
],
|
|
171
|
+
});
|
|
172
|
+
} else {
|
|
173
|
+
setColumnsState(params, { state: [] });
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// GRID
|
|
178
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
179
|
+
const gridDiv = document.querySelector("#myGrid");
|
|
180
|
+
if (!gridDiv) return;
|
|
181
|
+
|
|
182
|
+
// Theme class + real height before createGrid
|
|
183
|
+
gridDiv.classList.add('ag-theme-quartz');
|
|
184
|
+
if (!gridDiv.style.height) gridDiv.style.height = '600px';
|
|
185
|
+
if (!gridDiv.style.width) gridDiv.style.width = '100%';
|
|
186
|
+
|
|
187
|
+
const gridOptions = {
|
|
188
|
+
theme: iisTheme,
|
|
189
|
+
defaultColDef: {
|
|
190
|
+
resizable: true,
|
|
191
|
+
sortable: true,
|
|
192
|
+
filter: true,
|
|
193
|
+
flex: 1,
|
|
194
|
+
minWidth: 150,
|
|
195
|
+
unSortIcon: true, // show sort affordance when unsorted
|
|
196
|
+
},
|
|
197
|
+
columnDefs: [], // will be set after we inspect the JSON
|
|
198
|
+
async onGridReady(params) {
|
|
199
|
+
try {
|
|
200
|
+
// 1) Read the data-attribute (relative to THIS JS file)
|
|
201
|
+
// Example in HTML: <div id="myGrid" data-json="./table.json"></div>
|
|
202
|
+
const attr = gridDiv.dataset.json || './table.json';
|
|
203
|
+
const jsonUrl = new URL(attr, import.meta.url).toString();
|
|
204
|
+
|
|
205
|
+
// 2) Dynamic JSON import (works in modern bundlers)
|
|
206
|
+
// Some bundlers need the comment to skip pre-bundling the URL:
|
|
207
|
+
// and some require the JSON import assertion (uncomment if needed).
|
|
208
|
+
let mod;
|
|
209
|
+
try {
|
|
210
|
+
mod = await import(/* @vite-ignore */ jsonUrl);
|
|
211
|
+
} catch (e1) {
|
|
212
|
+
// Fallback with JSON import assertion (enable if your bundler requires it)
|
|
213
|
+
// mod = await import(/* @vite-ignore */ jsonUrl, { assert: { type: 'json' } });
|
|
214
|
+
throw e1; // if you want to rely solely on the first form, keep this
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const data = mod?.default ?? mod; // Vite/Webpack expose JSON on .default
|
|
218
|
+
const cols = buildColumnDefsFromData(data);
|
|
219
|
+
|
|
220
|
+
params.api.setGridOption('columnDefs', cols);
|
|
221
|
+
params.api.setGridOption('rowData', data);
|
|
222
|
+
|
|
223
|
+
params.api.sizeColumnsToFit();
|
|
224
|
+
} catch (err) {
|
|
225
|
+
console.error('Dynamic JSON import failed:', err);
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
onGridSizeChanged(params) {
|
|
229
|
+
applyResponsiveVisibility(params, gridDiv);
|
|
230
|
+
params.api.sizeColumnsToFit();
|
|
231
|
+
},
|
|
232
|
+
animateRows: true,
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
agGrid.createGrid(gridDiv, gridOptions);
|
|
236
|
+
|
|
237
|
+
// If the grid might be created while hidden, keep it healthy
|
|
238
|
+
const ro = new ResizeObserver(() => {
|
|
239
|
+
const api = gridOptions.api;
|
|
240
|
+
if (api) {
|
|
241
|
+
api.onGridSizeChanged();
|
|
242
|
+
api.sizeColumnsToFit();
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
ro.observe(gridDiv);
|
|
246
|
+
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
@charset "UTF-8";
|
|
2
2
|
@use "sass:color";
|
|
3
|
+
@use "sass:math";
|
|
3
4
|
@use '../../configurations/mixins' as mixin;
|
|
4
5
|
@use '../../configurations/extends';
|
|
5
6
|
@use '../../configurations/bem' as bem;
|
|
@@ -9,25 +10,41 @@
|
|
|
9
10
|
@use '../../vendor/grid/breakpoints' as breakpoint;
|
|
10
11
|
|
|
11
12
|
@include mixin.organism(selectable) {
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
fieldset button {
|
|
14
|
+
margin-bottom: func.rhythm(1);
|
|
15
|
+
|
|
16
|
+
@include breakpoint.bp-up(md) {
|
|
17
|
+
margin-bottom: 0;
|
|
18
|
+
}
|
|
14
19
|
}
|
|
15
20
|
|
|
16
21
|
@include bem.e(item) {
|
|
17
22
|
border-top: 1px solid colors.$color-concrete;
|
|
18
|
-
padding-top: func.rhythm(
|
|
19
|
-
margin-top: func.rhythm(
|
|
23
|
+
padding-top: func.rhythm(2);
|
|
24
|
+
margin-top: func.rhythm(1.5);
|
|
25
|
+
|
|
26
|
+
@include breakpoint.bp-up(lg) {
|
|
27
|
+
padding-top: func.rhythm(4);
|
|
28
|
+
margin-top: func.rhythm(3);
|
|
29
|
+
}
|
|
20
30
|
|
|
21
31
|
.js &:not(:first-child):not([aria-hidden]) {
|
|
22
32
|
display: none;
|
|
23
33
|
}
|
|
34
|
+
|
|
35
|
+
&[aria-hidden="true"] {
|
|
36
|
+
display: none;
|
|
37
|
+
}
|
|
24
38
|
}
|
|
25
39
|
|
|
26
40
|
@include bem.m(padded) {
|
|
27
|
-
padding: func.rhythm(
|
|
41
|
+
padding: func.rhythm(2);
|
|
28
42
|
|
|
29
|
-
@include
|
|
30
|
-
|
|
43
|
+
@include bem.e(item) {
|
|
44
|
+
.wp-block-graph .wrapper {
|
|
45
|
+
padding-left: 0;
|
|
46
|
+
padding-right: 0;
|
|
47
|
+
}
|
|
31
48
|
}
|
|
32
49
|
}
|
|
33
50
|
|
|
@@ -42,4 +59,42 @@
|
|
|
42
59
|
@include bem.m(shadow-large) {
|
|
43
60
|
@include mixin.card-shadow(colors.$color-cyberspace, 0.2);
|
|
44
61
|
}
|
|
62
|
+
|
|
63
|
+
@include bem.m(article-indent) {
|
|
64
|
+
@include breakpoint.bp-up(lg) {
|
|
65
|
+
@include bem.e(item) {
|
|
66
|
+
> p:not(.preamble),
|
|
67
|
+
> h2,
|
|
68
|
+
> h3,
|
|
69
|
+
> h4,
|
|
70
|
+
> h5,
|
|
71
|
+
> h6,
|
|
72
|
+
> ul,
|
|
73
|
+
> ol,
|
|
74
|
+
> .wp-block-image > figure,
|
|
75
|
+
> figure,
|
|
76
|
+
> blockquote {
|
|
77
|
+
&:not(.alignfull):not(.alignleft):not(.alignright):not(.alignwide):not(.wp-block-iis-hero) {
|
|
78
|
+
max-width: 46.364%;
|
|
79
|
+
margin-right: math.div(var.$grid-gutter-width, 2);
|
|
80
|
+
margin-left: calc(var.$indent * 2 - func.rhythm(2));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
> [class*='meta'] {
|
|
85
|
+
margin-left: calc(var.$indent * 2 - func.rhythm(2));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
> p.preamble {
|
|
89
|
+
max-width: 46.364%;
|
|
90
|
+
margin-left: calc(var.$indent * 2 - func.rhythm(2));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.wp-block-iis-info {
|
|
94
|
+
max-width: calc(46.364% + #{(var.$indent * 2)});
|
|
95
|
+
margin-left: calc(var.$indent * 2 - func.rhythm(2));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
45
100
|
}
|