Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 13 additions & 7 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -34,34 +34,40 @@ <h3>Automation</h3>
</div>
</div>
</section>
<section id="issue-board" class="issue-board">
<h2>Issue Board</h2>
<section id="issue-board" class="issue-board" aria-labelledby="issue-board-heading">
<h2 id="issue-board-heading">Issue Board</h2>
<div class="issue-controls">
<button id="new-issue-btn" class="cta-button">Create New Issue</button>
</div>
<div id="issue-form" class="issue-form" style="display: none;">
<div id="issue-form" class="issue-form" style="display: none;" aria-hidden="true">
<h3>Create New Issue</h3>
<form id="issue-create-form">
<form id="issue-create-form" aria-labelledby="issue-create-form-heading">
<h4 id="issue-create-form-heading" class="sr-only">Create Issue Form</h4>
<div class="form-group">
<label for="issue-title">Title</label>
<input type="text" id="issue-title" required>
<input type="text" id="issue-title" required aria-required="true">
<div id="title-error" class="error-message" aria-live="polite"></div>
</div>
<div class="form-group">
<label for="issue-description">Description</label>
<textarea id="issue-description" rows="4" required></textarea>
<textarea id="issue-description" rows="4" required aria-required="true"></textarea>
<div id="description-error" class="error-message" aria-live="polite"></div>
</div>
<div class="form-group">
<label for="issue-priority">Priority</label>
<select id="issue-priority">
<select id="issue-priority" required aria-required="true">
<option value="">Select priority level</option>
<option value="low">Low</option>
<option value="medium">Medium</option>
<option value="high">High</option>
</select>
<div id="priority-error" class="error-message" aria-live="polite"></div>
</div>
<div class="form-actions">
<button type="submit" class="cta-button">Create Issue</button>
<button type="button" id="cancel-issue-btn" class="cancel-button">Cancel</button>
</div>
<div id="form-status" aria-live="assertive"></div>
</form>
</div>
<div id="issues-list" class="issues-list">
Expand Down
143 changes: 124 additions & 19 deletions public/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,54 +9,113 @@ document.addEventListener('DOMContentLoaded', function() {
const cancelIssueBtn = document.getElementById('cancel-issue-btn');
const issueCreateForm = document.getElementById('issue-create-form');
const issuesList = document.getElementById('issues-list');
const formStatus = document.getElementById('form-status');

// Show/hide issue form
// Show/hide issue form with proper ARIA attributes
if (newIssueBtn) {
newIssueBtn.addEventListener('click', function() {
issueForm.style.display = issueForm.style.display === 'none' ? 'block' : 'none';
if (issueForm.style.display === 'block') {
const isOpen = issueForm.style.display === 'block';
issueForm.style.display = isOpen ? 'none' : 'block';
issueForm.setAttribute('aria-hidden', String(isOpen));

if (!isOpen) {
document.getElementById('issue-title').focus();
}
});

// Handle keyboard shortcut (Escape) to close form
newIssueBtn.addEventListener('keydown', function(e) {
if (e.key === 'Escape' && issueForm.style.display === 'block') {
issueForm.style.display = 'none';
issueForm.setAttribute('aria-hidden', 'true');
newIssueBtn.focus();
}
});
}

// Cancel issue creation
if (cancelIssueBtn) {
cancelIssueBtn.addEventListener('click', function() {
issueForm.style.display = 'none';
issueForm.setAttribute('aria-hidden', 'true');
issueCreateForm.reset();
newIssueBtn.focus();
});

// Handle Enter key on cancel button
cancelIssueBtn.addEventListener('keydown', function(e) {
if (e.key === 'Enter') {
e.preventDefault();
issueForm.style.display = 'none';
issueForm.setAttribute('aria-hidden', 'true');
issueCreateForm.reset();
newIssueBtn.focus();
}
});
}

// Handle issue form submission
// Handle issue form submission with validation
if (issueCreateForm) {
issueCreateForm.addEventListener('submit', function(e) {
e.preventDefault();

const title = document.getElementById('issue-title').value;
const description = document.getElementById('issue-description').value;
const priority = document.getElementById('issue-priority').value;
// Reset error messages
const errorMessages = ['title-error', 'description-error', 'priority-error'];
errorMessages.forEach(id => {
const el = document.getElementById(id);
if (el) el.textContent = '';
});

// Validate form
let isValid = true;
const title = document.getElementById('issue-title');
const description = document.getElementById('issue-description');
const priority = document.getElementById('issue-priority');

if (!title.value.trim()) {
document.getElementById('title-error').textContent = 'Title is required';
isValid = false;
title.focus();
} else if (!description.value.trim()) {
document.getElementById('description-error').textContent = 'Description is required';
isValid = false;
description.focus();
} else if (!priority.value) {
document.getElementById('priority-error').textContent = 'Priority is required';
isValid = false;
priority.focus();
}

if (!isValid) {
formStatus.textContent = 'Please correct the errors above';
formStatus.setAttribute('role', 'alert');
return;
}

// Create issue element
const issueItem = document.createElement('div');
issueItem.className = 'issue-item';
issueItem.setAttribute('tabindex', '0'); // Make it focusable
issueItem.setAttribute('role', 'article'); // Semantic role
issueItem.innerHTML = `
<h4>${title}</h4>
<h4>${title.value}</h4>
<div class="issue-meta">
<span>Priority: ${priority}</span>
<span>Priority: ${priority.value}</span>
<span>Created: ${new Date().toLocaleString()}</span>
</div>
<p class="issue-description">${description}</p>
<p class="issue-description">${description.value}</p>
`;

// Add click functionality for issue selection
// Add keyboard and click functionality for issue selection
issueItem.addEventListener('click', function() {
// Remove selected class from all issues
document.querySelectorAll('.issue-item').forEach(item => {
item.classList.remove('selected');
});
// Add selected class to clicked issue
this.classList.add('selected');
selectIssue(this);
});

issueItem.addEventListener('keydown', function(e) {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
selectIssue(this);
}
});

// Add to issues list
Expand All @@ -65,10 +124,41 @@ document.addEventListener('DOMContentLoaded', function() {
// Reset form and hide
issueCreateForm.reset();
issueForm.style.display = 'none';
issueForm.setAttribute('aria-hidden', 'true');

// Show success message
formStatus.textContent = 'Issue created successfully!';
formStatus.setAttribute('role', 'status');

// Show success message (optional)
alert('Issue created successfully!');
// Focus on newly created issue
setTimeout(() => {
issueItem.focus();
formStatus.textContent = '';
}, 500);
});

// Handle form reset on escape key
issueCreateForm.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
e.preventDefault();
issueForm.style.display = 'none';
issueForm.setAttribute('aria-hidden', 'true');
issueCreateForm.reset();
newIssueBtn.focus();
}
});
}

// Function to handle issue selection
function selectIssue(issueElement) {
// Remove selected class from all issues
document.querySelectorAll('.issue-item').forEach(item => {
item.classList.remove('selected');
item.setAttribute('aria-selected', 'false');
});
// Add selected class to clicked issue
issueElement.classList.add('selected');
issueElement.setAttribute('aria-selected', 'true');
}

// Example: smooth scrolling for anchor links
Expand All @@ -83,4 +173,19 @@ document.addEventListener('DOMContentLoaded', function() {
}
});
});

// Add focus indicators for better keyboard navigation
const focusableElements = document.querySelectorAll('button, input, textarea, select, a[href], [tabindex]:not([tabindex="-1"])');
focusableElements.forEach(el => {
el.addEventListener('focus', function() {
if (el.tagName === 'BUTTON' || el.tagName === 'INPUT' || el.tagName === 'TEXTAREA' || el.tagName === 'SELECT') {
el.style.outline = '2px solid var(--accent-color)';
el.style.outlineOffset = '2px';
}
});

el.addEventListener('blur', function() {
el.style.outline = '';
});
});
});
12 changes: 12 additions & 0 deletions public/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,18 @@
--form-border: #555555;
}

/* Screen reader only utility class */
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
}

* {
margin: 0;
padding: 0;
Expand Down