Skip to content

Commit c0b990a

Browse files
Merge pull request #9616 from circleci/DOCSS-1872-datatables
Use simple datatables
2 parents 5929f07 + 2e2f947 commit c0b990a

File tree

10 files changed

+475
-199
lines changed

10 files changed

+475
-199
lines changed

docs/guides/modules/ROOT/partials/pipelines-and-triggers/pipeline-values.adoc

Lines changed: 244 additions & 199 deletions
Large diffs are not rendered by default.

ui/gulp.d/tasks/build.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ module.exports = (src, dest, preview) => () => {
7979
.pipe(map((file, enc, next) => next(null, Object.assign(file, { extname: '' }, { extname: '.js' })))),
8080
// NOTE use the next line to bundle a JavaScript library that cannot be browserified, like jQuery
8181
//vfs.src(require.resolve('<package-name-or-require-path>'), opts).pipe(concat('js/vendor/<library-name>.js')),
82+
vfs.src(require.resolve('simple-datatables/dist/umd/simple-datatables.js'), opts).pipe(concat('js/vendor/simple-datatables.js')),
8283
vfs
8384
.src(['css/site.css', 'css/vendor/*.css'], { ...opts, sourcemaps })
8485
.pipe(postcss((file) => ({ plugins: postcssPlugins, options: { file } }))),

ui/package-lock.json

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ui/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
"@fontsource/ibm-plex-mono": "^5.2.5",
7575
"@fontsource/inter": "^5.2.5",
7676
"@tailwindcss/postcss": "^4.1.4",
77+
"simple-datatables": "^9.2.2",
7778
"tailwindcss": "^4.1.4"
7879
}
7980
}

ui/src/css/datatables.css

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/* Simple DataTables integration with CircleCI theme */
2+
3+
/* Add spacing above the top controls - this one we know works with lowercase */
4+
.datatable-top {
5+
@apply mt-6;
6+
}
7+
8+
/* Search box styling - matches header search exactly */
9+
.datatable-search input,
10+
.datatable-input {
11+
@apply focus:outline-none focus:ring-2 focus:ring-vapor focus:border-transparent;
12+
@apply py-2 pl-3 pr-4 border border-vapor rounded-full text-sm;
13+
@apply w-64 max-w-xs ml-1 mr-1;
14+
}
15+
16+
/* Table styling enhancements */
17+
table.datatable {
18+
@apply w-full border-collapse;
19+
}
20+
21+
table.datatable thead th {
22+
@apply px-4 py-3 text-left border-b border-gray-200;
23+
}
24+
25+
table.datatable thead th:hover {
26+
@apply bg-gray-100;
27+
}
28+
29+
/* Simple DataTables handles sorting indicators automatically */
30+
table.datatable thead th[data-sortable] {
31+
@apply cursor-pointer select-none;
32+
min-width: 80px; /* Minimum width to prevent overlap */
33+
}
34+
35+
table.datatable thead th[data-sortable]:hover {
36+
@apply bg-gray-100;
37+
}
38+
39+
/* Style the datatable-sorter button with DataTables-style arrows */
40+
table.datatable thead th .datatable-sorter {
41+
@apply w-full text-left bg-transparent border-0 p-0;
42+
@apply hover:bg-transparent focus:outline-none cursor-pointer;
43+
@apply pr-6 transition-colors; /* Add right padding to make room for sort arrows */
44+
position: relative;
45+
}
46+
47+
/* DataTables-style sorting indicators */
48+
table.datatable thead th[data-sortable="true"] .datatable-sorter::before {
49+
content: '';
50+
@apply absolute right-2;
51+
top: calc(50% - 9px);
52+
width: 0;
53+
height: 0;
54+
border-left: 4px solid transparent;
55+
border-right: 4px solid transparent;
56+
border-bottom: 4px solid #d1d5db;
57+
}
58+
59+
table.datatable thead th[data-sortable="true"] .datatable-sorter::after {
60+
content: '';
61+
@apply absolute right-2 top-1/2 transform -translate-y-1/2;
62+
width: 0;
63+
height: 0;
64+
border-left: 4px solid transparent;
65+
border-right: 4px solid transparent;
66+
border-top: 4px solid #d1d5db;
67+
}
68+
69+
table.datatable thead th[data-sortable="true"]:hover .datatable-sorter::after,
70+
table.datatable thead th[data-sortable="true"]:hover .datatable-sorter::before {
71+
border-top-color: #9ca3af;
72+
border-bottom-color: #9ca3af;
73+
}
74+
75+
table.datatable tbody td {
76+
@apply px-4 py-3 border-b border-gray-200;
77+
}
78+
79+
table.datatable tbody tr:hover {
80+
@apply bg-gray-50;
81+
}
82+

ui/src/css/doc.css

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1439,4 +1439,15 @@
14391439
.doc .circle-red {
14401440
color: #8F001C !important;
14411441
font-weight: var(--heading-font-weight);
1442+
}
1443+
1444+
/* Prevent code text from wrapping in table cells */
1445+
td .pre,
1446+
td .code,
1447+
td code,
1448+
td pre,
1449+
td .tableblock code,
1450+
td p code,
1451+
.tableblock code {
1452+
white-space: nowrap !important;
14421453
}

ui/src/css/site.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
@import "highlight.css";
1111
@import "print.css";
1212
@import "tabs.css";
13+
@import "datatables.css";
1314

1415
@source inline("translate-x-0");
1516
@source inline("h-dvh");
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@import "simple-datatables/dist/style.css";

ui/src/js/11-datatables.js

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
// Simple DataTables initialization for sortable/searchable tables
2+
;(function () {
3+
'use strict'
4+
5+
// Initialize Simple DataTables on tables with specific class
6+
function initializeDataTables() {
7+
const tables = document.querySelectorAll('table.datatable')
8+
9+
if (tables.length === 0) {
10+
console.log('No tables with .datatable class found')
11+
return
12+
}
13+
14+
// Check if Simple DataTables is available globally
15+
if (typeof simpleDatatables === 'undefined' && typeof window.simpleDatatables === 'undefined') {
16+
console.log('Simple DataTables not yet available, waiting...')
17+
setTimeout(initializeDataTables, 100)
18+
return
19+
}
20+
21+
// Get the DataTable constructor
22+
const DataTable = simpleDatatables?.DataTable || window.simpleDatatables?.DataTable || window.DataTable
23+
24+
if (!DataTable) {
25+
console.error('DataTable constructor not found!')
26+
return
27+
}
28+
29+
console.log(`Found ${tables.length} table(s) with .datatable class`)
30+
31+
tables.forEach((table, index) => {
32+
try {
33+
console.log(`Initializing Simple DataTable ${index + 1} of ${tables.length}`)
34+
35+
// Ensure table has a thead for proper functionality
36+
if (!table.querySelector('thead')) {
37+
console.warn(`Table ${index + 1} does not have a thead element. Simple DataTables may not work properly.`)
38+
}
39+
40+
// Check for CSS classes that control column sorting
41+
let columnConfig = undefined
42+
43+
// Check for specific no-sort classes (e.g., .no-sort-col-3)
44+
const classList = Array.from(table.classList)
45+
const noSortClasses = classList.filter(cls => cls.startsWith('no-sort-col-'))
46+
47+
if (noSortClasses.length > 0) {
48+
const totalColumns = table.querySelector('thead tr').children.length
49+
const nonSortableColumns = noSortClasses.map(cls => {
50+
const match = cls.match(/no-sort-col-(\d+)/)
51+
return match ? parseInt(match[1]) : null
52+
}).filter(col => col !== null)
53+
54+
console.log(`Non-sortable columns:`, nonSortableColumns)
55+
console.log(`Total columns:`, totalColumns)
56+
57+
columnConfig = []
58+
for (let i = 0; i < totalColumns; i++) {
59+
const isSortable = !nonSortableColumns.includes(i)
60+
columnConfig.push({
61+
select: i,
62+
sortable: isSortable
63+
})
64+
console.log(`Column ${i}: sortable = ${isSortable}`)
65+
}
66+
}
67+
68+
// Alternative: Check for .no-sort-description class (makes column 3 non-sortable for pipeline tables)
69+
else if (table.classList.contains('no-sort-description')) {
70+
const totalColumns = table.querySelector('thead tr').children.length
71+
columnConfig = []
72+
for (let i = 0; i < totalColumns; i++) {
73+
// For pipeline tables, column 3 is typically the description/value column
74+
const isSortable = i !== 3
75+
columnConfig.push({
76+
select: i,
77+
sortable: isSortable
78+
})
79+
console.log(`Column ${i}: sortable = ${isSortable} (description column rule)`)
80+
}
81+
}
82+
83+
new DataTable(table, {
84+
searchable: true,
85+
sortable: true,
86+
paging: false,
87+
perPage: 25,
88+
perPageSelect: [10, 25, 50, 100],
89+
labels: {
90+
placeholder: "Filter records...",
91+
perPage: "Show {select} entries per page",
92+
noRows: "No data available in table",
93+
info: "Showing {start} to {end} of {rows} entries"
94+
},
95+
columns: columnConfig
96+
})
97+
98+
console.log(`Simple DataTable initialized successfully for table ${index + 1}`)
99+
} catch (error) {
100+
console.error(`Error initializing Simple DataTable for table ${index + 1}:`, error)
101+
}
102+
})
103+
}
104+
105+
// Initialize when DOM is ready
106+
if (document.readyState === 'loading') {
107+
document.addEventListener('DOMContentLoaded', initializeDataTables)
108+
} else {
109+
initializeDataTables()
110+
}
111+
112+
console.log('Simple DataTables module loaded')
113+
})()

ui/src/partials/footer-scripts.hbs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
<script src="{{{uiRootPath}}}/js/vendor/simple-datatables.js"></script>
12
<script id="site-script" src="{{{uiRootPath}}}/js/site.js" data-ui-root-path="{{{uiRootPath}}}"></script>
23
<script async src="{{{uiRootPath}}}/js/vendor/highlight.js"></script>
34
{{#if env.SITE_SEARCH_PROVIDER}}

0 commit comments

Comments
 (0)