fix(error): harden logging, i18n, a11y and handler tests
This commit is contained in:
@@ -156,6 +156,10 @@ body.app-error-debug {
|
||||
|
||||
.app-error-debug-copy-button:hover { color: var(--app-color); }
|
||||
.app-error-debug-copy-button.copied { color: var(--app-primary); }
|
||||
.app-error-debug-copy-button:focus-visible {
|
||||
outline: 2px solid var(--app-primary);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
/* ── Exception Summary ───────────────────────────────────────────────── */
|
||||
|
||||
@@ -206,6 +210,10 @@ body.app-error-debug {
|
||||
}
|
||||
|
||||
.app-error-debug-tab:hover { color: var(--app-color); }
|
||||
.app-error-debug-tab:focus-visible {
|
||||
outline: 2px solid var(--app-primary);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.app-error-debug-tab.active {
|
||||
color: var(--app-primary);
|
||||
@@ -251,11 +259,18 @@ body.app-error-debug {
|
||||
cursor: pointer;
|
||||
background: var(--app-card-background-color);
|
||||
transition: background var(--app-transition);
|
||||
width: 100%;
|
||||
border: 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.app-error-debug-frame__header:hover {
|
||||
background: var(--app-card-sectioning-background-color);
|
||||
}
|
||||
.app-error-debug-frame__header:focus-visible {
|
||||
outline: 2px solid var(--app-primary);
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
.app-error-debug-frame__index {
|
||||
color: var(--app-muted-color);
|
||||
@@ -385,6 +400,19 @@ body.app-error-debug {
|
||||
cursor: pointer;
|
||||
font-family: var(--app-font-family-monospace);
|
||||
font-size: var(--text-xs);
|
||||
width: 100%;
|
||||
border: 0;
|
||||
text-align: left;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.app-error-debug-query__header:hover {
|
||||
background: var(--app-card-sectioning-background-color);
|
||||
}
|
||||
|
||||
.app-error-debug-query__header:focus-visible {
|
||||
outline: 2px solid var(--app-primary);
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
.app-error-debug-query__duration {
|
||||
|
||||
@@ -5,32 +5,74 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
const tabs = document.querySelectorAll('.app-error-debug-tab');
|
||||
const panels = document.querySelectorAll('.app-error-debug-panel');
|
||||
|
||||
const activateTab = (tab) => {
|
||||
const target = tab.dataset.panel;
|
||||
tabs.forEach(t => t.classList.remove('active'));
|
||||
tabs.forEach(t => t.setAttribute('aria-selected', 'false'));
|
||||
tabs.forEach(t => t.setAttribute('tabindex', '-1'));
|
||||
panels.forEach(p => p.classList.remove('active'));
|
||||
tab.classList.add('active');
|
||||
tab.setAttribute('aria-selected', 'true');
|
||||
tab.setAttribute('tabindex', '0');
|
||||
const panel = document.getElementById(target);
|
||||
if (panel) panel.classList.add('active');
|
||||
};
|
||||
|
||||
tabs.forEach(tab => {
|
||||
tab.addEventListener('click', () => {
|
||||
const target = tab.dataset.panel;
|
||||
tabs.forEach(t => t.classList.remove('active'));
|
||||
panels.forEach(p => p.classList.remove('active'));
|
||||
tab.classList.add('active');
|
||||
const panel = document.getElementById(target);
|
||||
if (panel) panel.classList.add('active');
|
||||
activateTab(tab);
|
||||
});
|
||||
|
||||
tab.addEventListener('keydown', (event) => {
|
||||
if (tabs.length === 0) return;
|
||||
const currentIndex = Array.prototype.indexOf.call(tabs, tab);
|
||||
if (currentIndex < 0) return;
|
||||
|
||||
let nextIndex = currentIndex;
|
||||
if (event.key === 'ArrowRight') {
|
||||
nextIndex = (currentIndex + 1) % tabs.length;
|
||||
} else if (event.key === 'ArrowLeft') {
|
||||
nextIndex = (currentIndex - 1 + tabs.length) % tabs.length;
|
||||
} else if (event.key === 'Home') {
|
||||
nextIndex = 0;
|
||||
} else if (event.key === 'End') {
|
||||
nextIndex = tabs.length - 1;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
const nextTab = tabs[nextIndex];
|
||||
if (!nextTab) return;
|
||||
activateTab(nextTab);
|
||||
nextTab.focus();
|
||||
});
|
||||
});
|
||||
|
||||
// ── Frame expand/collapse ──────────────────────────
|
||||
document.querySelectorAll('.app-error-debug-frame__header').forEach(header => {
|
||||
header.addEventListener('click', () => {
|
||||
header.closest('.app-error-debug-frame')?.classList.toggle('open');
|
||||
document.querySelectorAll('.app-error-debug-frame__toggle').forEach(toggle => {
|
||||
toggle.addEventListener('click', () => {
|
||||
const frame = toggle.closest('.app-error-debug-frame');
|
||||
if (!frame) return;
|
||||
const isOpen = frame.classList.toggle('open');
|
||||
toggle.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
|
||||
});
|
||||
});
|
||||
|
||||
// Open the first frame by default.
|
||||
const firstFrame = document.querySelector('.app-error-debug-frame');
|
||||
if (firstFrame) firstFrame.classList.add('open');
|
||||
// Ensure ARIA reflects initial state.
|
||||
document.querySelectorAll('.app-error-debug-frame').forEach(frame => {
|
||||
const toggle = frame.querySelector('.app-error-debug-frame__toggle');
|
||||
if (!toggle) return;
|
||||
toggle.setAttribute('aria-expanded', frame.classList.contains('open') ? 'true' : 'false');
|
||||
});
|
||||
|
||||
// ── Query expand/collapse ──────────────────────────
|
||||
document.querySelectorAll('.app-error-debug-query__header').forEach(header => {
|
||||
header.addEventListener('click', () => {
|
||||
header.closest('.app-error-debug-query')?.classList.toggle('open');
|
||||
document.querySelectorAll('.app-error-debug-query__toggle').forEach(toggle => {
|
||||
toggle.addEventListener('click', () => {
|
||||
const query = toggle.closest('.app-error-debug-query');
|
||||
if (!query) return;
|
||||
const isOpen = query.classList.toggle('open');
|
||||
toggle.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user