WIP: enhance validation feedback in code editor, add support for multiple validation indicators and new validation types
This commit is contained in:
38
src/app.js
38
src/app.js
@@ -299,6 +299,38 @@ function runCode() {
|
||||
|
||||
const validationResult = validateUserCode(userCode, lesson);
|
||||
|
||||
// Remove any existing validation indicators before adding new ones
|
||||
const existingIndicators = elements.codeEditor.querySelectorAll(".validation-success-indicator");
|
||||
existingIndicators.forEach((indicator) => indicator.remove());
|
||||
|
||||
// Add validation indicators based on validCases count if available
|
||||
if (validationResult.validCases) {
|
||||
const casesCount =
|
||||
typeof validationResult.validCases === "number"
|
||||
? validationResult.validCases
|
||||
: Array.isArray(validationResult.validCases)
|
||||
? validationResult.validCases.length
|
||||
: 1;
|
||||
|
||||
// Create a container for indicators if it doesn't exist
|
||||
let indicatorContainer = elements.codeEditor.querySelector(".validation-indicators-container");
|
||||
if (!indicatorContainer) {
|
||||
indicatorContainer = document.createElement("div");
|
||||
indicatorContainer.className = "validation-indicators-container";
|
||||
elements.codeEditor.appendChild(indicatorContainer);
|
||||
} else {
|
||||
indicatorContainer.innerHTML = "";
|
||||
}
|
||||
|
||||
// Add the appropriate number of checkmarks
|
||||
for (let i = 0; i < casesCount; i++) {
|
||||
const validationIndicator = document.createElement("div");
|
||||
validationIndicator.className = "validation-success-indicator";
|
||||
validationIndicator.innerHTML = "✓";
|
||||
indicatorContainer.appendChild(validationIndicator);
|
||||
}
|
||||
}
|
||||
|
||||
if (validationResult.isValid) {
|
||||
// Mark lesson as completed
|
||||
const moduleProgress = state.userProgress[state.currentModule.id];
|
||||
@@ -308,12 +340,6 @@ function runCode() {
|
||||
updateModuleSelectorButtonProgress();
|
||||
}
|
||||
|
||||
// Add validation indicator to editor
|
||||
const validationIndicator = document.createElement("div");
|
||||
validationIndicator.className = "validation-success-indicator";
|
||||
validationIndicator.innerHTML = "✓";
|
||||
elements.codeEditor.appendChild(validationIndicator);
|
||||
|
||||
// Show success feedback with visual indicators
|
||||
showFeedback(true, validationResult.message || "Great job! Your code works correctly.");
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ import responsiveConfig from "../../lessons/08-responsive.json";
|
||||
|
||||
// Module store
|
||||
const moduleStore = [
|
||||
basicSelectorsConfig
|
||||
basicSelectorsConfig,
|
||||
// basicsConfig,
|
||||
// boxModelConfig,
|
||||
// selectorsConfig,
|
||||
|
||||
@@ -19,50 +19,82 @@ export function validateUserCode(userCode, lesson) {
|
||||
// Default validation result
|
||||
let result = {
|
||||
isValid: true,
|
||||
validCases: 0,
|
||||
message: "Your code looks good!"
|
||||
};
|
||||
|
||||
// Process each validation rule
|
||||
for (const validation of validations) {
|
||||
const { type, value, message, options } = validation;
|
||||
let validationPassed = false;
|
||||
|
||||
switch (type) {
|
||||
case "contains":
|
||||
if (!containsValidation(userCode, value, options)) {
|
||||
return { isValid: false, message: message || `Your code should include "${value}".` };
|
||||
validationPassed = containsValidation(userCode, value, options);
|
||||
if (!validationPassed) {
|
||||
result = {
|
||||
isValid: false,
|
||||
validCases: result.validCases,
|
||||
message: message || `Your code should include "${value}".`
|
||||
};
|
||||
}
|
||||
break;
|
||||
|
||||
case "not_contains":
|
||||
if (containsValidation(userCode, value, options)) {
|
||||
return { isValid: false, message: message || `Your code should not include "${value}".` };
|
||||
validationPassed = !containsValidation(userCode, value, options);
|
||||
if (!validationPassed) {
|
||||
result = {
|
||||
isValid: false,
|
||||
validCases: result.validCases,
|
||||
message: message || `Your code should not include "${value}".`
|
||||
};
|
||||
}
|
||||
break;
|
||||
|
||||
case "regex":
|
||||
if (!regexValidation(userCode, value, options)) {
|
||||
return { isValid: false, message: message || "Your code does not match the expected pattern." };
|
||||
validationPassed = regexValidation(userCode, value, options);
|
||||
if (!validationPassed) {
|
||||
result = {
|
||||
isValid: false,
|
||||
validCases: result.validCases,
|
||||
message: message || "Your code does not match the expected pattern."
|
||||
};
|
||||
}
|
||||
break;
|
||||
|
||||
case "property_value":
|
||||
if (!propertyValueValidation(userCode, value, options)) {
|
||||
return { isValid: false, message: message || `The "${value.property}" property should be set to "${value.expected}".` };
|
||||
validationPassed = propertyValueValidation(userCode, value, options);
|
||||
if (!validationPassed) {
|
||||
result = {
|
||||
isValid: false,
|
||||
validCases: result.validCases,
|
||||
message: message || `The "${value.property}" property should be set to "${value.expected}".`
|
||||
};
|
||||
}
|
||||
break;
|
||||
|
||||
case "syntax":
|
||||
const syntaxResult = syntaxValidation(userCode);
|
||||
if (!syntaxResult.isValid) {
|
||||
return { isValid: false, message: message || `CSS syntax error: ${syntaxResult.error}` };
|
||||
validationPassed = syntaxResult.isValid;
|
||||
if (!validationPassed) {
|
||||
result = {
|
||||
isValid: false,
|
||||
validCases: result.validCases,
|
||||
message: message || `CSS syntax error: ${syntaxResult.error}`
|
||||
};
|
||||
}
|
||||
break;
|
||||
|
||||
case "custom":
|
||||
if (validation.validator && typeof validation.validator === "function") {
|
||||
const customResult = validation.validator(userCode);
|
||||
if (!customResult.isValid) {
|
||||
return { isValid: false, message: customResult.message || message || "Your code does not meet the requirements." };
|
||||
validationPassed = customResult.isValid;
|
||||
if (!validationPassed) {
|
||||
result = {
|
||||
isValid: false,
|
||||
validCases: result.validCases,
|
||||
message: customResult.message || message || "Your code does not meet the requirements."
|
||||
};
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -71,10 +103,22 @@ export function validateUserCode(userCode, lesson) {
|
||||
|
||||
default:
|
||||
console.warn(`Unknown validation type: ${type}`);
|
||||
continue; // Skip counting this validation
|
||||
}
|
||||
|
||||
// Count valid cases
|
||||
if (validationPassed) {
|
||||
result.validCases++;
|
||||
}
|
||||
|
||||
// Return early if validation failed
|
||||
if (!validationPassed) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// If we've passed all validations, return success
|
||||
// If we've passed all validations, return success with all cases passed
|
||||
result.validCases = validations.length;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
<div class="code-editor">
|
||||
<div class="editor-header">
|
||||
<span>CSS Editor</span>
|
||||
<div class="validation-indicators-container"></div>
|
||||
<button id="run-btn" class="btn btn-secondary"><img src="./gear.svg" />Run</button>
|
||||
</div>
|
||||
<div class="editor-content">
|
||||
|
||||
18
src/main.css
18
src/main.css
@@ -343,15 +343,6 @@ code {
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.code-input.inline-input {
|
||||
display: inline-block;
|
||||
min-height: 30px;
|
||||
height: auto;
|
||||
padding: 2px 4px;
|
||||
margin: 0 4px;
|
||||
border-bottom: 1px dashed #666;
|
||||
}
|
||||
|
||||
/* Controls */
|
||||
.controls {
|
||||
display: flex;
|
||||
@@ -424,10 +415,16 @@ code {
|
||||
border: 1px solid var(--success-color);
|
||||
}
|
||||
|
||||
.validation-success-indicator {
|
||||
.validation-indicators-container {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
right: 7rem;
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.validation-success-indicator {
|
||||
background-color: var(--success-color);
|
||||
color: white;
|
||||
width: 20px;
|
||||
@@ -437,7 +434,6 @@ code {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 12px;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
/* Module selector button with progress */
|
||||
|
||||
Reference in New Issue
Block a user