Skip to content

Commit 1afec5b

Browse files
committed
Merge branch 'feature/standard-module-ci'
* feature/standard-module-ci: Fix jest tests Add compiled assets for testing Add nvm for jest tests Fix tests Switch to standard Silverstripe module CI
2 parents 5490df5 + c237ebb commit 1afec5b

File tree

10 files changed

+6555
-330
lines changed

10 files changed

+6555
-330
lines changed

.github/workflows/ci.yml

Lines changed: 6 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -2,110 +2,12 @@ name: CI
22

33
on:
44
push:
5-
branches: [ main, master ]
65
pull_request:
7-
branches: [ main, master ]
6+
workflow_dispatch:
87

98
jobs:
10-
javascript-tests:
11-
name: JavaScript Tests
12-
runs-on: ubuntu-latest
13-
14-
strategy:
15-
matrix:
16-
node-version: [16.x, 18.x, 20.x]
17-
18-
steps:
19-
- uses: actions/checkout@v3
20-
21-
- name: Use Node.js ${{ matrix.node-version }}
22-
uses: actions/setup-node@v3
23-
with:
24-
node-version: ${{ matrix.node-version }}
25-
cache: 'npm'
26-
27-
- name: Install dependencies
28-
run: npm ci
29-
30-
- name: Run linter
31-
run: npm run lint
32-
33-
- name: Run tests with coverage
34-
run: npm run test:js:coverage
35-
36-
- name: Upload coverage to Codecov
37-
uses: codecov/codecov-action@v3
38-
with:
39-
files: ./coverage/lcov.info
40-
flags: javascript
41-
42-
php-tests:
43-
name: PHP Tests
44-
runs-on: ubuntu-latest
45-
46-
strategy:
47-
matrix:
48-
php-version: ['7.4', '8.0', '8.1', '8.2']
49-
silverstripe-version: ['^4.0', '^5.0']
50-
exclude:
51-
- php-version: '7.4'
52-
silverstripe-version: '^5.0'
53-
54-
steps:
55-
- uses: actions/checkout@v3
56-
57-
- name: Setup PHP
58-
uses: shivammathur/setup-php@v2
59-
with:
60-
php-version: ${{ matrix.php-version }}
61-
extensions: mbstring, intl, pdo, pdo_mysql
62-
coverage: xdebug
63-
64-
- name: Validate composer.json
65-
run: composer validate --strict
66-
67-
- name: Cache Composer packages
68-
uses: actions/cache@v3
69-
with:
70-
path: vendor
71-
key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
72-
restore-keys: |
73-
${{ runner.os }}-php-
74-
75-
- name: Install dependencies
76-
run: |
77-
composer require silverstripe/framework:${{ matrix.silverstripe-version }} --no-update
78-
composer install --prefer-dist --no-progress
79-
80-
- name: Run test suite
81-
run: vendor/bin/phpunit --coverage-clover coverage.xml
82-
83-
- name: Upload coverage to Codecov
84-
uses: codecov/codecov-action@v3
85-
with:
86-
files: ./coverage.xml
87-
flags: php
88-
89-
build:
90-
name: Build Assets
91-
runs-on: ubuntu-latest
92-
93-
steps:
94-
- uses: actions/checkout@v3
95-
96-
- name: Use Node.js
97-
uses: actions/setup-node@v3
98-
with:
99-
node-version: '18.x'
100-
cache: 'npm'
101-
102-
- name: Install dependencies
103-
run: npm ci
104-
105-
- name: Build production assets
106-
run: npm run build
107-
108-
- name: Verify build output
109-
run: |
110-
test -f client/dist/queuedjobs-admin.js || exit 1
111-
test -f client/dist/queuedjobs-admin.css || exit 1
9+
ci:
10+
name: CI
11+
# Do not run if this is a pull-request from same repo i.e. not a fork repo
12+
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.repository
13+
uses: silverstripe/gha-ci/.github/workflows/ci.yml@v2

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
node_modules/
2-
client/dist/
32
npm-debug.log
43
yarn-error.log
54
.DS_Store

.nvmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
20

client/dist/queuedjobs-admin.css

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
/**
2+
* Queued Jobs Live Refresh - Styles
3+
*
4+
* Provides styling for the play/pause toggle button and notification system.
5+
*/
6+
7+
/* ==========================================================================
8+
Play/Pause Toggle Button
9+
========================================================================== */
10+
11+
/**
12+
* Base button styling - subtle outline style that matches SilverStripe admin UI
13+
* WCAG 2.2 AA compliant: min 44x44px touch target, sufficient contrast
14+
*/
15+
.queuedjobs-live-toggle {
16+
padding: 8px 12px;
17+
display: inline-flex;
18+
align-items: center;
19+
justify-content: center;
20+
margin-top: -4px;
21+
margin-right: 8px;
22+
vertical-align: middle;
23+
min-width: 44px;
24+
min-height: 44px;
25+
position: relative;
26+
}
27+
28+
/* Screen reader only text */
29+
.queuedjobs-live-toggle .visually-hidden {
30+
position: absolute;
31+
width: 1px;
32+
height: 1px;
33+
padding: 0;
34+
margin: -1px;
35+
overflow: hidden;
36+
clip: rect(0, 0, 0, 0);
37+
white-space: nowrap;
38+
border: 0;
39+
}
40+
41+
/* Spin the icon when refreshing */
42+
.queuedjobs-live-toggle.refreshing .toggle-icon {
43+
animation: spin 1s linear infinite;
44+
animation-delay: 200ms;
45+
}
46+
47+
/* Icon styling with smooth transitions */
48+
.queuedjobs-live-toggle .toggle-icon {
49+
display: inline-block;
50+
font-size: 16px;
51+
line-height: 1;
52+
width: 16px;
53+
text-align: center;
54+
transition: 100ms opacity 150ms ease-in-out;
55+
opacity: 1;
56+
}
57+
58+
/* ==========================================================================
59+
Animations
60+
========================================================================== */
61+
62+
/* Spin animation for refresh indicator */
63+
@keyframes spin {
64+
to {
65+
transform: rotate(360deg);
66+
}
67+
}
68+
69+
/* ==========================================================================
70+
Notification System
71+
========================================================================== */
72+
73+
/**
74+
* Error/info notification that slides in from the right
75+
* WCAG 2.2 AA compliant: role="alert", sufficient contrast, dismissible
76+
*/
77+
.queuedjobs-live-notification {
78+
position: fixed;
79+
top: 20px;
80+
right: 20px;
81+
background: #d32f2f;
82+
color: white;
83+
padding: 12px 40px 12px 16px;
84+
border-radius: 4px;
85+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
86+
z-index: 10001;
87+
max-width: 400px;
88+
animation: slideIn 0.3s ease;
89+
font-size: 14px;
90+
line-height: 1.5;
91+
}
92+
93+
/* Slide-in animation */
94+
@keyframes slideIn {
95+
from {
96+
transform: translateX(100%);
97+
opacity: 0;
98+
}
99+
to {
100+
transform: translateX(0);
101+
opacity: 1;
102+
}
103+
}
104+
105+
/* Close button within notification - WCAG 2.2 AA: 24x24px minimum */
106+
.queuedjobs-live-notification .close-btn {
107+
position: absolute;
108+
top: 8px;
109+
right: 8px;
110+
background: rgba(255, 255, 255, 0.2);
111+
border: 1px solid rgba(255, 255, 255, 0.3);
112+
border-radius: 3px;
113+
color: white;
114+
font-size: 20px;
115+
line-height: 1;
116+
cursor: pointer;
117+
padding: 0;
118+
min-width: 24px;
119+
min-height: 24px;
120+
display: flex;
121+
align-items: center;
122+
justify-content: center;
123+
}
124+
125+
.queuedjobs-live-notification .close-btn:hover,
126+
.queuedjobs-live-notification .close-btn:focus {
127+
background: rgba(255, 255, 255, 0.3);
128+
outline: 2px solid white;
129+
outline-offset: 2px;
130+
}
131+
132+
/* ==========================================================================
133+
Layout
134+
========================================================================== */
135+
136+
/* Button is inserted directly into DOM, no container needed */
137+
138+
/* ==========================================================================
139+
Responsive Design
140+
========================================================================== */
141+
142+
@media (max-width: 768px) {
143+
/* Smaller button on mobile */
144+
.queuedjobs-live-toggle {
145+
padding: 5px 10px;
146+
font-size: 12px;
147+
}
148+
149+
/* Full-width notifications on mobile */
150+
.queuedjobs-live-notification {
151+
top: 10px;
152+
right: 10px;
153+
left: 10px;
154+
max-width: none;
155+
}
156+
}

client/dist/queuedjobs-admin.js

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

client/src/queuedjobs-admin.js

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
* @author Ed Wilde
99
* @version 1.0.0
1010
*/
11+
/* global jQuery */
1112
(function() {
1213
'use strict';
1314

@@ -50,12 +51,12 @@
5051
// SilverStripe CMS uses jQuery PJAX, so we need multiple event listeners
5152
document.addEventListener('pjax:end', () => this.setup());
5253
window.addEventListener('cms-content-loaded', () => this.setup());
53-
54+
5455
// Also listen on jQuery if available (SilverStripe uses jQuery PJAX)
5556
if (typeof jQuery !== 'undefined') {
5657
jQuery(document).on('pjax:end', () => this.setup());
5758
}
58-
59+
5960
// Periodically check if button needs to be created (fallback)
6061
// This handles cases where events don't fire reliably
6162
setInterval(() => {
@@ -116,20 +117,20 @@
116117
* Insert the button immediately before the filter button.
117118
* Handles different DOM structures that may occur during GridField updates.
118119
* Hides button if filter area is open (no filter button visible).
119-
*
120+
*
120121
* @param {HTMLElement} button - The button element to insert
121122
*/
122123
insertButtonBeforeFilter(button) {
123124
const filterButton = document.querySelector('button[name="showFilter"], .grid-field__filter-open');
124125
const gridField = document.querySelector('.ss-gridfield, .grid-field');
125-
126+
126127
if (!filterButton) {
127128
// Check if filter is open (has show-filter class)
128129
if (gridField && gridField.classList.contains('show-filter')) {
129130
// Filter is open, hide our button
130131
button.style.display = 'none';
131132
// Still need to insert it somewhere to keep reference
132-
const toolbar = document.querySelector('.cms-content-toolbar') ||
133+
const toolbar = document.querySelector('.cms-content-toolbar') ||
133134
document.querySelector('.cms-content-header');
134135
if (toolbar) {
135136
toolbar.appendChild(button);
@@ -192,11 +193,11 @@
192193
const form = document.querySelector('.queuedjobs-live-enabled');
193194
if (!form) return;
194195

195-
const observer = new MutationObserver((mutations) => {
196+
const observer = new MutationObserver(() => {
196197
const button = document.querySelector('.queuedjobs-live-toggle');
197198
const filterButton = document.querySelector('button[name="showFilter"], .grid-field__filter-open');
198199
const gridField = document.querySelector('.ss-gridfield, .grid-field');
199-
200+
200201
if (!button) {
201202
// Button was removed, re-create it
202203
this.createToggleButton();
@@ -207,7 +208,7 @@
207208
} else {
208209
// Check if filter is open/closed
209210
const filterIsOpen = gridField && gridField.classList.contains('show-filter');
210-
211+
211212
if (filterIsOpen && !filterButton) {
212213
// Filter is open, hide button
213214
button.style.display = 'none';
@@ -313,7 +314,7 @@
313314

314315
// Fade out (100ms transition)
315316
icon.style.opacity = '0';
316-
317+
317318
// Wait for fade out (100ms) + pause (200ms) before changing content
318319
setTimeout(() => {
319320
if (this.isPolling) {
@@ -329,16 +330,16 @@
329330
this.toggleButton.setAttribute('aria-label', 'Toggle auto-refresh. Currently stopped.');
330331
this.toggleButton.setAttribute('aria-pressed', 'false');
331332
}
332-
333+
333334
// Update transition for fade in
334335
icon.style.transition = 'opacity 0.15s ease';
335-
336+
336337
// Trigger reflow to ensure transition applies
337338
void icon.offsetHeight;
338-
339+
339340
// Fade in (150ms transition)
340341
icon.style.opacity = '1';
341-
342+
342343
// Reset transition back to default after fade in completes
343344
setTimeout(() => {
344345
icon.style.transition = '';
@@ -494,7 +495,7 @@
494495
notification.className = 'queuedjobs-live-notification';
495496
notification.setAttribute('role', 'alert');
496497
notification.setAttribute('aria-live', 'assertive');
497-
498+
498499
const messageSpan = document.createElement('span');
499500
messageSpan.textContent = message;
500501
notification.appendChild(messageSpan);

0 commit comments

Comments
 (0)