initial commit

This commit is contained in:
2026-03-16 19:54:53 -04:00
commit bfe0e01254
3341 changed files with 483939 additions and 0 deletions

View File

@@ -0,0 +1,189 @@
# Dev Mode - Usage Guide
**Purpose**: Easy feedback on prototypes by copying Object IDs to clipboard
---
## 🎯 What is Dev Mode?
Dev Mode is a built-in feature in all WDS prototypes that allows testers, stakeholders, and designers to easily reference specific UI elements when providing feedback.
Instead of saying *"The button in the top right"*, you can say *"Fix `customer-sign-bankid`"* - precise and unambiguous!
---
## 🚀 How to Use
### Step 1: Activate Dev Mode
**Two ways**:
1. Click the **Dev Mode button** (top-right corner)
2. Press **Ctrl+E** on your keyboard
The button will turn blue and say **"Dev Mode: ON"**
---
### Step 2: Find the Element
- **Hover** over any element you want to reference
- You'll see a **gray outline** appear
- A **tooltip** shows the Object ID
**Prototype still works normally!** You can click buttons, fill forms, etc.
---
### Step 3: Copy the Object ID
- **Hold the Shift key** (outline turns **green**)
- **Click the element** while holding Shift
- **Object ID is copied!** ✓
You'll see a green success message: **"✓ Copied: [object-id]"**
**Important**: Shift key is **disabled when typing in form fields** (input, textarea, etc.) so you can type capital letters and special characters normally!
---
### Step 4: Paste in Feedback
Now paste the Object ID in your feedback:
**Good feedback**:
```
❌ Issue with `customer-sign-bankid`:
The button is disabled even after I check the consent checkbox.
💡 Suggestion for `sidebar-video`:
Make the video auto-play on mobile.
```
**Developer knows EXACTLY** which element you're talking about!
---
## 🎨 Visual Guide
| State | Appearance | Action |
|-------|------------|--------|
| **Dev Mode OFF** | Normal prototype | Click button or press Ctrl+E |
| **Dev Mode ON (hovering)** | Gray outline | Shows Object ID in tooltip |
| **Shift held (hovering)** | Green outline | Click to copy |
| **After copying** | Green flash | Object ID in clipboard |
---
## ⌨️ Keyboard Shortcuts
- **Ctrl+E**: Toggle Dev Mode on/off
- **Shift + Click**: Copy Object ID (when dev mode is on)
---
## 💡 Tips
1. **Activate once**, then navigate through prototype normally
2. **Hold Shift only when copying** - prototype works without it
3. **Type in fields normally** - Shift is disabled when focused on input/textarea
4. **Deactivate when done** testing (Ctrl+E again)
5. **Object IDs are permanent** - always refer to the same element
---
## 📋 Example Workflow
### Tester's Perspective:
1. Open prototype
2. Press **Ctrl+E** (Dev Mode on)
3. Test the prototype normally
4. Find a bug - hover over problem element
5. Hold **Shift**, click element
6. Paste Object ID into bug report: "`customer-facility-startdate-group` shows wrong default date"
7. Continue testing
### Designer's Perspective:
Receives feedback:
```
Bug: `customer-facility-startdate-group` shows wrong default date
```
- Open prototype
- Press **Ctrl+F** in browser, search for `customer-facility-startdate-group`
- Find exact element in code
- Fix the date calculation
- Done! ✅
---
## 🔧 For Developers
When you receive Object IDs in feedback:
1. Open the HTML file
2. Search for the Object ID (Ctrl+F)
3. Element is either:
- `id="object-id"` attribute
- `data-object-id="object-id"` attribute
4. Fix the issue in that specific element
---
## ❓ FAQs
**Q: Does Dev Mode affect the prototype?**
A: No! The prototype works normally. You need to hold Shift to copy IDs.
**Q: Can I use this on mobile?**
A: Yes! The button appears on mobile too. Use a Bluetooth keyboard or on-screen Shift key.
**Q: Can I type in form fields while Dev Mode is on?**
A: Yes! Shift key is automatically disabled when you're typing in input fields or textareas, so you can type capital letters and special characters normally.
**Q: What if an element doesn't have an ID?**
A: Dev Mode walks up the tree to find the nearest parent with an ID.
**Q: Can I copy multiple IDs?**
A: Yes! Hold Shift, click first element, release Shift, hold again, click second element, etc.
**Q: Is this only for bugs?**
A: No! Use it for any feedback - bugs, suggestions, questions, clarifications.
---
## 🎓 Best Practices
### For Testers:
-**DO**: Include Object ID in every piece of feedback
-**DO**: Test prototype normally, copy IDs when needed
-**DO**: Combine Object ID with description
-**DON'T**: Leave Dev Mode on during normal use
### For Designers:
-**DO**: Ensure all interactive elements have Object IDs
-**DO**: Use descriptive, consistent naming
-**DO**: Include Dev Mode in all prototypes
-**DON'T**: Change Object IDs after sharing prototype
---
## 🚨 Troubleshooting
**Problem**: Dev Mode button not showing
**Solution**: Check that `dev-mode.js` and `dev-mode.css` are loaded
**Problem**: Clicking doesn't copy
**Solution**: Make sure you're holding **Shift** while clicking
**Problem**: Tooltip not showing
**Solution**: Element might not have an ID - check console logs
**Problem**: Can't turn off Dev Mode
**Solution**: Press Ctrl+E or refresh the page
---
**Dev Mode makes feedback precise, fast, and frustration-free!** 🎯

View File

@@ -0,0 +1,164 @@
/* ============================================================================
PROTOTYPE DEV MODE STYLES
Styles for developer/feedback mode that allows copying Object IDs
Usage: Include these styles in your prototype HTML or CSS file
============================================================================ */
/* Dev Mode Toggle Button */
.dev-mode-toggle {
position: fixed;
top: 20px;
right: 20px;
z-index: 9999;
background: #fff;
border: 2px solid #e5e7eb;
border-radius: 8px;
padding: 10px 16px;
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
transition: all 0.2s;
font-size: 14px;
font-weight: 500;
color: #6b7280;
}
.dev-mode-toggle:hover {
background: #f9fafb;
border-color: #d1d5db;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.dev-mode-toggle.active {
background: #0066CC;
border-color: #0066CC;
color: #fff;
box-shadow: 0 4px 12px rgba(0, 102, 204, 0.3);
}
.dev-mode-toggle svg {
flex-shrink: 0;
}
/* Dev Mode Active State */
body.dev-mode-active {
cursor: help !important; /* Show help cursor to indicate special mode */
}
/* Subtle element highlighting on hover (not Shift held) */
body.dev-mode-active [id]:hover {
outline: 2px solid #6b7280 !important;
outline-offset: 2px !important;
box-shadow: 0 0 0 2px rgba(107, 114, 128, 0.2) !important;
}
/* Active highlighting when Shift is held (ready to copy) */
body.dev-mode-active.shift-held {
cursor: copy !important;
}
body.dev-mode-active.shift-held [id]:hover {
outline: 3px solid #10B981 !important;
outline-offset: 3px !important;
box-shadow: 0 0 0 5px rgba(16, 185, 129, 0.4) !important;
}
/* Dev Mode Tooltip */
.dev-mode-tooltip {
position: fixed;
background: #1F2937;
color: #fff;
padding: 8px 12px;
border-radius: 6px;
font-size: 13px;
font-weight: 600;
font-family: 'Courier New', monospace;
z-index: 10000;
pointer-events: none;
white-space: nowrap;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
transition: background 0.2s;
}
.dev-mode-tooltip::before {
content: '';
position: absolute;
top: -4px;
left: 8px;
width: 8px;
height: 8px;
background: inherit;
transform: rotate(45deg);
}
/* Disable only certain interactions when Shift is held in dev mode */
body.dev-mode-active.shift-held button:not(#dev-mode-toggle),
body.dev-mode-active.shift-held input,
body.dev-mode-active.shift-held select,
body.dev-mode-active.shift-held textarea,
body.dev-mode-active.shift-held a {
pointer-events: none !important;
}
/* But allow the toggle button to work */
body.dev-mode-active #dev-mode-toggle,
body.dev-mode-active #dev-mode-toggle * {
pointer-events: auto !important;
cursor: pointer !important;
}
/* Feedback overlay (created dynamically) */
@keyframes fadeInOut {
0% {
opacity: 0;
transform: translate(-50%, -50%) scale(0.9);
}
20% {
opacity: 1;
transform: translate(-50%, -50%) scale(1);
}
80% {
opacity: 1;
transform: translate(-50%, -50%) scale(1);
}
100% {
opacity: 0;
transform: translate(-50%, -50%) scale(0.9);
}
}
/* Responsive: Adjust toggle button on mobile */
@media (max-width: 768px) {
.dev-mode-toggle {
top: 10px;
right: 10px;
padding: 8px 12px;
font-size: 12px;
}
.dev-mode-toggle span {
display: none; /* Hide text on mobile, show only icon */
}
.dev-mode-toggle.active span {
display: inline; /* Show "ON" status */
max-width: 60px;
}
}
/* Optional: Add visual indicator when Shift is held */
body.dev-mode-active.shift-held .dev-mode-toggle::after {
content: '⬆️';
margin-left: 4px;
animation: pulse 1s infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.7; transform: scale(1.1); }
}

View File

@@ -0,0 +1,18 @@
<!-- ============================================================================
PROTOTYPE DEV MODE - HTML SNIPPET
Add this HTML to your prototype page (inside <body>, preferably at the top)
============================================================================ -->
<!-- Dev Mode Toggle Button (fixed position, top-right) -->
<button id="dev-mode-toggle" class="dev-mode-toggle" title="Toggle Dev Mode (Ctrl+E)">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path>
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path>
</svg>
<span>Dev Mode: OFF</span>
</button>
<!-- Dev Mode Tooltip (shown when hovering over elements in dev mode) -->
<div id="dev-mode-tooltip" class="dev-mode-tooltip" style="display: none;"></div>

View File

@@ -0,0 +1,430 @@
/* eslint-disable n/no-unsupported-features/node-builtins */
/* global document, window */
/**
* PROTOTYPE DEV MODE
*
* Developer/feedback mode that allows users to easily copy Object IDs to clipboard
* for providing precise feedback on prototype elements.
*
* Features:
* - Toggle dev mode with button or Ctrl+E
* - Prototype works NORMALLY when dev mode is on
* - Hold Shift + Click any element to copy its Object ID
* - Visual highlights show what will be copied (green when Shift is held)
* - Tooltip shows Object ID on hover
* - Success feedback when copied
*
* Usage:
* 1. Include this script in your prototype HTML
* 2. Add the HTML toggle button and tooltip (see HTML template)
* 3. Add the CSS styles (see CSS template)
* 4. Call initDevMode() on page load
*
* How it works:
* - Activate dev mode (Ctrl+E or click button)
* - Hover over elements to see their Object IDs (gray outline)
* - Hold Shift key (outline turns green)
* - Click while holding Shift to copy Object ID
* - Prototype works normally without Shift held
* - **Shift is disabled when typing in form fields** (input, textarea, etc.)
*/
// ============================================================================
// DEV MODE STATE
// ============================================================================
let devModeActive = false;
let shiftKeyPressed = false;
let currentHighlightedElement = null;
// ============================================================================
// INITIALIZATION
// ============================================================================
function initDevMode() {
const toggleButton = document.querySelector('#dev-mode-toggle');
const tooltip = document.querySelector('#dev-mode-tooltip');
if (!toggleButton || !tooltip) {
console.warn('⚠️ Dev Mode: Toggle button or tooltip not found');
return;
}
// Check if user agent supports clipboard API
if (typeof navigator !== 'undefined' && navigator.clipboard) {
// Clipboard API available
} else {
console.warn('⚠️ Clipboard API not supported in this browser');
return;
}
setupKeyboardShortcuts();
setupToggleButton(toggleButton, tooltip);
setupHoverHighlight(tooltip);
setupClickCopy();
console.log('%c💡 Dev Mode available: Press Ctrl+E or click the Dev Mode button', 'color: #0066CC; font-weight: bold;');
}
// ============================================================================
// KEYBOARD SHORTCUTS
// ============================================================================
function setupKeyboardShortcuts() {
// Track Shift key for container selection
document.addEventListener('keydown', (e) => {
if (e.key === 'Shift') {
// Don't activate if user is typing in a form field
if (isTypingInField()) {
return;
}
shiftKeyPressed = true;
document.body.classList.add('shift-held');
if (devModeActive) {
console.log('%c⬆ Shift held: Click any element to copy its Object ID', 'color: #10B981; font-weight: bold;');
}
}
// Ctrl+E toggle
if (e.ctrlKey && e.key === 'e') {
e.preventDefault();
document.querySelector('#dev-mode-toggle')?.click();
}
});
document.addEventListener('keyup', (e) => {
if (e.key === 'Shift') {
shiftKeyPressed = false;
document.body.classList.remove('shift-held');
if (devModeActive) {
console.log('%c⬇ Shift released: Prototype works normally (hold Shift to copy)', 'color: #6b7280;');
}
}
});
}
// ============================================================================
// TOGGLE BUTTON
// ============================================================================
function setupToggleButton(toggleButton, tooltip) {
toggleButton.addEventListener('click', function (e) {
e.stopPropagation();
if (typeof globalThis !== 'undefined') {
globalThis.devModeActive = true;
} else if (globalThis.window !== undefined) {
globalThis.devModeActive = true;
}
devModeActive = !devModeActive;
// Update UI
document.body.classList.toggle('dev-mode-active', devModeActive);
toggleButton.classList.toggle('active', devModeActive);
const statusText = toggleButton.querySelector('span');
if (statusText) {
statusText.textContent = devModeActive ? 'Dev Mode: ON' : 'Dev Mode: OFF';
}
// Log status
console.log(`🔧 Dev Mode: ${devModeActive ? 'ACTIVATED' : 'DEACTIVATED'}`);
if (devModeActive) {
console.log('%c🔧 DEV MODE ACTIVE', 'color: #0066CC; font-size: 16px; font-weight: bold;');
console.log('%c⚠ Hold SHIFT + Click any element to copy its Object ID', 'color: #FFB800; font-size: 14px; font-weight: bold;');
console.log('%cWithout Shift: Prototype works normally', 'color: #6b7280;');
console.log('%cPress Ctrl+E to toggle Dev Mode', 'color: #6b7280;');
} else {
tooltip.style.display = 'none';
if (currentHighlightedElement) {
clearHighlight();
}
}
});
}
// ============================================================================
// HOVER HIGHLIGHT
// ============================================================================
function setupHoverHighlight(tooltip) {
// Show tooltip and highlight on hover
document.addEventListener('mouseover', function (e) {
if (!devModeActive) return;
// Don't highlight if user is typing in a field
if (isTypingInField()) {
tooltip.style.display = 'none';
clearHighlight();
return;
}
clearHighlight();
let element = findElementWithId(e.target);
if (!element || !element.id || isSystemElement(element.id)) {
tooltip.style.display = 'none';
return;
}
// Highlight element
highlightElement(element, shiftKeyPressed);
currentHighlightedElement = element;
// Show tooltip
const prefix = shiftKeyPressed ? '✓ Click to Copy: ' : '⬆️ Hold Shift + Click: ';
tooltip.textContent = prefix + element.id;
tooltip.style.display = 'block';
tooltip.style.background = shiftKeyPressed ? '#10B981' : '#6b7280';
tooltip.style.color = '#fff';
updateTooltipPosition(e, tooltip);
});
// Update tooltip position on mouse move
document.addEventListener('mousemove', function (e) {
if (devModeActive && tooltip.style.display === 'block') {
updateTooltipPosition(e, tooltip);
}
});
// Clear highlight on mouse out
document.addEventListener('mouseout', function (e) {
if (!devModeActive) return;
if (e.target.id) {
tooltip.style.display = 'none';
clearHighlight();
}
});
}
// ============================================================================
// CLICK TO COPY
// ============================================================================
function setupClickCopy() {
// Use capture phase to intercept clicks with Shift
document.addEventListener(
'click',
function (e) {
if (!devModeActive) return;
// Allow toggle button to work normally
if (isToggleButton(e.target)) return;
// ONLY copy if Shift is held
if (!shiftKeyPressed) {
// Let prototype work normally without Shift
return;
}
// Don't intercept if user is clicking in/around a form field
if (isTypingInField() || isFormElement(e.target)) {
return;
}
// Shift is held and not in a form field - intercept and copy
e.preventDefault();
e.stopPropagation();
e.stopImmediatePropagation();
let element = findElementWithId(e.target);
if (!element || !element.id || isSystemElement(element.id)) {
console.log('❌ No Object ID found');
return false;
}
// Copy to clipboard
const objectId = element.id;
copyToClipboard(objectId);
// Show feedback
showCopyFeedback(element, objectId);
return false;
},
true,
); // Capture phase
}
// ============================================================================
// HELPER FUNCTIONS
// ============================================================================
function findElementWithId(element) {
let current = element;
let attempts = 0;
while (current && !current.id && attempts < 10) {
current = current.parentElement;
attempts++;
}
return current;
}
function isSystemElement(id) {
const systemIds = ['app', 'dev-mode-toggle', 'dev-mode-tooltip'];
return systemIds.includes(id);
}
function isToggleButton(element) {
return element.id === 'dev-mode-toggle' || element.closest('#dev-mode-toggle') || element.classList.contains('dev-mode-toggle');
}
function isTypingInField() {
const activeElement = document.activeElement;
if (!activeElement) return false;
const tagName = activeElement.tagName.toLowerCase();
const isEditable = activeElement.isContentEditable;
// Check if user is currently typing in a form field
return tagName === 'input' || tagName === 'textarea' || tagName === 'select' || isEditable;
}
function isFormElement(element) {
if (!element) return false;
const tagName = element.tagName.toLowerCase();
const isEditable = element.isContentEditable;
// Check if the clicked element is a form element
return tagName === 'input' || tagName === 'textarea' || tagName === 'select' || isEditable;
}
function highlightElement(element, isShiftHeld) {
const color = isShiftHeld ? '#10B981' : '#6b7280';
const width = isShiftHeld ? '3px' : '2px';
const offset = isShiftHeld ? '3px' : '2px';
const shadowSpread = isShiftHeld ? '5px' : '2px';
const shadowOpacity = isShiftHeld ? '0.4' : '0.2';
element.style.outline = `${width} solid ${color}`;
element.style.outlineOffset = offset;
element.style.boxShadow = `0 0 0 ${shadowSpread} rgba(${isShiftHeld ? '16, 185, 129' : '107, 114, 128'}, ${shadowOpacity})`;
}
function clearHighlight() {
if (currentHighlightedElement) {
currentHighlightedElement.style.outline = '';
currentHighlightedElement.style.boxShadow = '';
currentHighlightedElement = null;
}
}
function updateTooltipPosition(e, tooltip) {
const offset = 15;
let x = e.clientX + offset;
let y = e.clientY + offset;
// Keep tooltip on screen
const rect = tooltip.getBoundingClientRect();
if (x + rect.width > window.innerWidth) {
x = e.clientX - rect.width - offset;
}
if (y + rect.height > window.innerHeight) {
y = e.clientY - rect.height - offset;
}
tooltip.style.left = x + 'px';
tooltip.style.top = y + 'px';
}
function copyToClipboard(text) {
if (typeof navigator !== 'undefined' && navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard
.writeText(text)
.then(() => {
console.log(`📋 Copied to clipboard: ${text}`);
})
.catch((error) => {
console.error('Dev Mode error:', error);
fallbackCopy(text);
});
} else {
fallbackCopy(text);
}
}
function fallbackCopy(text) {
const textarea = document.createElement('textarea');
textarea.value = text;
textarea.style.position = 'fixed';
textarea.style.left = '-999999px';
document.body.append(textarea);
textarea.focus();
textarea.select();
try {
document.execCommand('copy');
console.log(`📋 Copied (fallback): ${text}`);
} catch (error) {
console.error('Dev Mode error:', error);
}
textarea.remove();
}
function showCopyFeedback(element, objectId) {
// Create feedback overlay
const feedback = document.createElement('div');
feedback.textContent = '✓ Copied: ' + objectId;
feedback.style.cssText = `
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: #10B981;
color: #fff;
padding: 16px 32px;
border-radius: 8px;
font-size: 16px;
font-weight: 600;
z-index: 100000;
box-shadow: 0 10px 25px rgba(0,0,0,0.3);
animation: fadeInOut 1.5s ease-in-out;
pointer-events: none;
`;
document.body.append(feedback);
setTimeout(() => {
feedback.remove();
}, 1500);
// Flash element
const originalOutline = element.style.outline;
element.style.outline = '3px solid #10B981';
setTimeout(() => {
element.style.outline = originalOutline;
}, 300);
}
// Add CSS animation
const style = document.createElement('style');
style.textContent = `
@keyframes fadeInOut {
0% { opacity: 0; transform: translate(-50%, -50%) scale(0.9); }
20% { opacity: 1; transform: translate(-50%, -50%) scale(1); }
80% { opacity: 1; transform: translate(-50%, -50%) scale(1); }
100% { opacity: 0; transform: translate(-50%, -50%) scale(0.9); }
}
`;
document.head.append(style);
// ============================================================================
// EXPORT
// ============================================================================
// Make available globally
globalThis.initDevMode = initDevMode;
// Export for use in other scripts
if (typeof globalThis !== 'undefined' && globalThis.exports) {
globalThis.exports = { initDevMode };
}