Skip to content

Commit 8f58d50

Browse files
authored
Add AJV validation for theme.json exports (#14)
1 parent 9754b84 commit 8f58d50

File tree

10 files changed

+3460
-598
lines changed

10 files changed

+3460
-598
lines changed

export.html

Lines changed: 128 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -932,6 +932,49 @@
932932
padding: calc(var(--spacing) / 4);
933933
}
934934

935+
/* Validation Errors Styles */
936+
.validation-errors {
937+
background-color: #f8d7da;
938+
border: 1px solid #f5c2c7;
939+
border-radius: 4px;
940+
padding: calc(var(--spacing) / 2);
941+
margin-bottom: var(--spacing);
942+
display: none;
943+
}
944+
945+
.validation-errors.visible {
946+
display: block;
947+
}
948+
949+
.validation-errors-title {
950+
font-weight: 600;
951+
color: #842029;
952+
margin-bottom: calc(var(--spacing) / 4);
953+
display: flex;
954+
align-items: center;
955+
gap: calc(var(--spacing) / 2);
956+
font-size: 0.85rem;
957+
}
958+
959+
.validation-errors-list {
960+
list-style: none;
961+
padding: 0;
962+
margin: 0;
963+
max-height: 100px;
964+
overflow-y: auto;
965+
}
966+
967+
.validation-errors-list li {
968+
font-size: 0.8rem;
969+
color: #842029;
970+
padding: calc(var(--spacing) / 4) 0;
971+
border-bottom: 1px solid rgba(132, 32, 41, 0.2);
972+
}
973+
974+
.validation-errors-list li:last-child {
975+
border-bottom: none;
976+
}
977+
935978
/* Validation Warnings Styles */
936979
.validation-warnings {
937980
background-color: #fff3cd;
@@ -1167,6 +1210,15 @@ <h4 class="collections-group-title">Extended Settings</h4>
11671210
</div>
11681211
</div>
11691212

1213+
<!-- Validation Errors -->
1214+
<div class="validation-errors" id="validation-errors">
1215+
<div class="validation-errors-title">
1216+
Validation Errors
1217+
</div>
1218+
<ul class="validation-errors-list" id="validation-errors-list">
1219+
</ul>
1220+
</div>
1221+
11701222
<!-- Validation Warnings -->
11711223
<div class="validation-warnings" id="validation-warnings">
11721224
<div class="validation-warnings-title">
@@ -1182,6 +1234,7 @@ <h4 class="collections-group-title">Extended Settings</h4>
11821234

11831235
<div class="button-row">
11841236
<button id="export" type="button">Export Variables</button>
1237+
<button id="validate-export" type="button" class="secondary" style="display:none;">Validate Schema</button>
11851238
<button id="download" type="button">Download theme.json</button>
11861239
</div>
11871240
<div id="files-container">
@@ -1327,66 +1380,80 @@ <h3 class="modal-title">Customize Color Presets (Selected Collections)</h3>
13271380

13281381
currentFiles = pluginMessage.files;
13291382

1330-
// Handle validation warnings if present
1383+
// Handle validation errors and warnings if present
1384+
const validationErrors = pluginMessage.errors || [];
13311385
validationWarnings = pluginMessage.warnings || [];
1386+
displayValidationErrors(validationErrors);
13321387
displayValidationWarnings(validationWarnings);
1333-
1388+
1389+
// Show validate button now that we have export data
1390+
document.getElementById("validate-export").style.display = "inline-block";
1391+
13341392
// Clear the files container
13351393
const filesContainer = document.getElementById('files-container');
13361394
filesContainer.innerHTML = '';
1337-
1395+
13381396
// Add each file to the container
13391397
currentFiles.forEach((file, index) => {
13401398
const { fileName, body } = file;
13411399
const fileContent = JSON.stringify(body, null, 2);
1342-
1400+
13431401
// Create file container
13441402
const fileContainer = document.createElement('div');
13451403
fileContainer.className = 'file-container';
1346-
1404+
13471405
// Create file header with name and actions
13481406
const fileHeader = document.createElement('div');
13491407
fileHeader.className = 'file-header';
1350-
1408+
13511409
const fileName2 = document.createElement('div');
13521410
fileName2.className = 'file-name';
13531411
fileName2.textContent = fileName;
1354-
1412+
13551413
const fileActions = document.createElement('div');
13561414
fileActions.className = 'file-actions';
1357-
1415+
13581416
const copyButton = document.createElement('button');
13591417
copyButton.className = 'copy-button';
13601418
copyButton.textContent = 'Copy';
13611419
copyButton.onclick = () => copyToClipboard(index);
1362-
1420+
13631421
fileActions.appendChild(copyButton);
13641422
fileHeader.appendChild(fileName2);
13651423
fileHeader.appendChild(fileActions);
1366-
1424+
13671425
// Create textarea for the file content
13681426
const textarea = document.createElement('textarea');
13691427
textarea.readOnly = true;
13701428
textarea.id = `file-content-${index}`;
13711429
textarea.value = fileContent;
1372-
1430+
13731431
// Assemble the file container
13741432
fileContainer.appendChild(fileHeader);
13751433
fileContainer.appendChild(textarea);
1376-
1434+
13771435
// Add to files container
13781436
filesContainer.appendChild(fileContainer);
13791437
});
1380-
1438+
13811439
// Show download button if we have files
13821440
if (currentFiles.length > 0) {
13831441
document.getElementById("download").style.display = "block";
13841442
// Update button text to indicate it's a zip download if multiple files
13851443
const downloadBtn = document.getElementById("download");
1386-
downloadBtn.textContent = currentFiles.length > 1
1387-
? `Download Theme Files (${currentFiles.length} files)`
1444+
downloadBtn.textContent = currentFiles.length > 1
1445+
? `Download Theme Files (${currentFiles.length} files)`
13881446
: "Download theme.json";
13891447
}
1448+
} else if (pluginMessage.type === "VALIDATE_EXPORT_RESULT") {
1449+
if (pluginMessage.error) {
1450+
displayValidationErrors([pluginMessage.error]);
1451+
return;
1452+
}
1453+
1454+
const validation = pluginMessage.validation;
1455+
displayValidationErrors(validation.errors || []);
1456+
displayValidationWarnings(validation.warnings || []);
13901457
}
13911458
};
13921459

@@ -1598,7 +1665,31 @@ <h3 class="modal-title">Customize Color Presets (Selected Collections)</h3>
15981665
toggleIcon.classList.toggle('collapsed');
15991666
});
16001667

1601-
// Validation warnings functionality
1668+
// Validation display functionality
1669+
function displayValidationErrors(errors) {
1670+
const errorsContainer = document.getElementById('validation-errors');
1671+
const errorsList = document.getElementById('validation-errors-list');
1672+
1673+
if (!errors || errors.length === 0) {
1674+
errorsContainer.classList.remove('visible');
1675+
errorsList.innerHTML = '';
1676+
return;
1677+
}
1678+
1679+
errorsContainer.classList.add('visible');
1680+
const errorsTitle = errorsContainer.querySelector('.validation-errors-title');
1681+
errorsTitle.textContent = errors.length === 1
1682+
? '1 Validation Error'
1683+
: errors.length + ' Validation Errors';
1684+
1685+
errorsList.innerHTML = '';
1686+
errors.forEach(error => {
1687+
const li = document.createElement('li');
1688+
li.textContent = error;
1689+
errorsList.appendChild(li);
1690+
});
1691+
}
1692+
16021693
function displayValidationWarnings(warnings) {
16031694
const warningsContainer = document.getElementById('validation-warnings');
16041695
const warningsList = document.getElementById('validation-warnings-list');
@@ -1610,15 +1701,35 @@ <h3 class="modal-title">Customize Color Presets (Selected Collections)</h3>
16101701
}
16111702

16121703
warningsContainer.classList.add('visible');
1613-
warningsList.innerHTML = '';
1704+
const warningsTitle = warningsContainer.querySelector('.validation-warnings-title');
1705+
warningsTitle.textContent = warnings.length === 1
1706+
? '1 Validation Warning'
1707+
: warnings.length + ' Validation Warnings';
16141708

1709+
warningsList.innerHTML = '';
16151710
warnings.forEach(warning => {
16161711
const li = document.createElement('li');
16171712
li.textContent = warning;
16181713
warningsList.appendChild(li);
16191714
});
16201715
}
16211716

1717+
// Standalone validate button
1718+
document.getElementById('validate-export').addEventListener('click', () => {
1719+
if (!currentFiles || currentFiles.length === 0) return;
1720+
1721+
// Find the theme.json file
1722+
const themeFile = currentFiles.find(f => f.fileName === 'theme.json');
1723+
if (!themeFile) return;
1724+
1725+
parent.postMessage({
1726+
pluginMessage: {
1727+
type: "VALIDATE_EXPORT",
1728+
themeJson: themeFile.body
1729+
}
1730+
}, "*");
1731+
});
1732+
16221733
// Color customization functionality
16231734
const colorPresetsCheckbox = document.getElementById("generate-color-presets");
16241735
const customizeButton = document.getElementById("customize-colors");

0 commit comments

Comments
 (0)