Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
51d5de4
🎨 Palette: Enhance CustomCheckbox with grid support and required indi…
google-labs-jules[bot] Feb 27, 2026
c01bbb7
🎨 Palette: Improve My Contributions empty state and loading feedback
google-labs-jules[bot] Feb 28, 2026
b9f6e97
🎨 Palette: Standardize CustomCheckbox with grid and accessibility props
google-labs-jules[bot] Mar 1, 2026
f19e9a0
🎨 Palette: Improve V2 navigation and progress bar accessibility
google-labs-jules[bot] Mar 9, 2026
dad9800
🎨 Palette: Accessible Forms and Password Toggle in V2 Signup
google-labs-jules[bot] Mar 30, 2026
3319412
🎨 Palette: Enhance V2 Auth Forms Accessibility and UX
google-labs-jules[bot] Apr 13, 2026
5702811
feat(kb): add API-driven knowledge base
godie Apr 29, 2026
d46758d
🎨 Palette: Add keyboard shortcuts to flashcard study
google-labs-jules[bot] May 4, 2026
4f2d610
Merge remote-tracking branch 'origin/palette-flashcard-shortcuts-7158…
godie May 18, 2026
eef8273
Merge remote-tracking branch 'origin/migration/v2' into feat/unify-a1…
godie May 18, 2026
dfc48dc
Merge remote-tracking branch 'origin/palette-enhance-v2-auth-forms-12…
godie May 18, 2026
d5b3f92
Merge remote-tracking branch 'origin/palette-accessible-v2-forms-2914…
godie May 18, 2026
dc178b8
Merge remote-tracking branch 'origin/palette/v2-navi-progressbar-a11y…
godie May 18, 2026
5d0f01b
Merge remote-tracking branch 'origin/palette-custom-checkbox-ux-62670…
godie May 19, 2026
bc0a497
Merge remote-tracking branch 'origin/palette-my-contributions-ux-1472…
godie May 19, 2026
f29ae36
Merge remote-tracking branch 'origin/palette-custom-checkbox-enhancem…
godie May 19, 2026
fd44b37
fix: resolve merge issues - ExamenRow import, CustomCheckbox tests, K…
godie May 19, 2026
82c9cd8
fix(tests): correct localStorage mock in V2Navi.test to avoid navFreq…
godie May 19, 2026
e73f66e
fix(tests): eliminate act() and read-only field console warnings acro…
godie May 19, 2026
64d077d
fix(tests): eliminate remaining act() warnings in CasoForm, PlayerDas…
godie May 19, 2026
004f14a
fix(tests): add pattern-based console interceptor and suppress expect…
godie May 19, 2026
f0e9ff5
feat(ci): add pattern-based console warning interceptor and CI check
godie May 19, 2026
5d2a98a
feat(hooks): add pre-commit hook to run tests before commits
godie May 19, 2026
8487d2b
docs: document console warning patterns and __SKIP_CONSOLE_CHECKS__ i…
godie May 19, 2026
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
16 changes: 16 additions & 0 deletions .Jules/palette.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,19 @@
## 2025-06-25 - [Copy-to-Clipboard UX and Immediate Feedback]
**Learning:** Adding a copy-to-clipboard feature for key user identifiers (like email or IDs) significantly reduces friction in user workflows. Implementing this with `navigator.clipboard.writeText` and providing immediate visual feedback via a toast notification ('Correo copiado al portapapeles') confirms success. Using icon-only buttons with explicit `aria-label` and `title` ensures the feature is both accessible and intuitive.
**Action:** Always provide a copy-to-clipboard option for static text that users frequently need to reuse, and ensure immediate feedback is provided upon action.

## 2025-01-15 - [Keyboard Efficiency and Discoverability in Flashcards]
**Learning:** For high-repetition tasks like flashcard study, keyboard shortcuts (Space/Enter for flip, 1-4 for rating) dramatically improve user flow and "flow state" immersion. However, shortcuts must be discoverable; adding bracketed visual hints like "[Espacio]" or "[1]" directly to button labels, along with descriptive "aria-label" and "title" attributes, ensures accessibility and lowers the learning curve for power users.
**Action:** Always include keyboard shortcuts for core repetitive loops and provide integrated visual hints for those shortcuts to ensure they aren't "hidden" features.

## 2025-06-18 - [Standardized Accessibility and Password Visibility in V2 Auth Forms]
**Learning:** In V2 authentication flows, maintaining feature parity between Login, Signup, and Forgot Password pages—specifically regarding password visibility toggles and label-input associations—ensures a predictable and accessible user experience. Missing `id` and `htmlFor` attributes on inputs breaks screen reader navigation even if the layout looks correct.
**Action:** Always audit the full authentication suite (Login, Signup, Recovery) for accessibility parity and ensure interactive password fields consistently offer visibility toggles.

## 2025-06-17 - [Accessible Progress Indicators and V2 Navigation Polish]
**Learning:** For progress indicators to be truly accessible, they must include `role="progressbar"` along with `aria-valuenow`, `aria-valuemin`, and `aria-valuemax` attributes, allowing screen readers to accurately report status. In side-navigation rails, ensuring decorative icons are marked with `aria-hidden="true"` and the active link has `aria-current="page"` significantly improves screen reader navigation. Additionally, maintaining localization consistency (e.g., 'Inicio' vs 'Home') in new UI versions (V2) is essential for a cohesive user experience.
**Action:** Always implement full ARIA attributes for progress components and audit new navigation structures for both accessibility markers and language consistency.

## 2025-06-17 - [Accessible Checkboxes and Consistent Prop Spreading]
**Learning:** Enhancing base form components like `CustomCheckbox` with standardized `required` indicators (visual asterisk and `aria-required`) and grid support (`s`, `m`, `l`, `xl`, `offset`) improves both accessibility and developer productivity. Ensuring that `...props` are consistently applied to the inner `input` regardless of the presence of a wrapper `div` maintains a predictable component API.
**Action:** Always provide visual and semantic cues for mandatory fields and ensure consistent prop-spreading behavior in multi-layered components.
8 changes: 8 additions & 0 deletions .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,11 @@ jobs:

- name: Run tests
run: npm test

- name: Check for console warnings in test output
run: |
npm test 2>&1 | tee vitest-output.txt
if grep -qE 'not wrapped in act|changing an uncontrolled|does not recognize the.*prop|Received `.+?` for a non-boolean attribute|Invalid DOM property|Each child in a list should have a unique.*key' vitest-output.txt; then
echo "ERROR: Console warnings detected in test output!"
exit 1
fi
64 changes: 64 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,67 @@
## GitHub Actions
- The CI/CD pipeline is configured to use `npm`.
- Ensure `package-lock.json` is always updated and committed when changing dependencies.

## Testing Standards

### Console Warning Interceptor
All tests run through a global interceptor in `src/test/setupTests.js` that fails tests on specific console.error/console.warn patterns. This catches test-quality issues before they reach CI.

**Blocked patterns (console.error):**
| Pattern | Issue | Common Cause |
|---|---|---|
| `not wrapped in act` | Async state update after test ends | API mock resolves after `await waitFor(() => {})`; loading state mock never resolves |
| `changing an uncontrolled` | Input rendered without `onChange` handler | Mock form with initial undefined `value`; component renders without controlled prop |
| `does not recognize the.*prop` | Invalid prop passed to DOM element | Passing `noMargin` or other custom props directly to `<div>`/`<select>` |
| `checked.*onChange` / `value.*onChange` | Missing onChange on controlled input | Render with `checked` or `value` but no `onChange` |
| `Received \`.+?\` for a non-boolean attribute` | Boolean-like prop leaked to DOM | Passing `noMargin` or similar to DOM elements |
| `Invalid DOM property` | Wrong prop casing on DOM element | Using camelCase prop that should be kebab-case |
| `validateDOMNesting` | Invalid DOM nesting | `<a>` inside `<a>`, `<form>` inside `<form>`, etc. |
| `Each child in a list should have a unique.*key` | Missing key in list render | Map over items without `key` prop |
| `value prop on %s should not be null` | Null value on input | Passing `null` to `value` prop |

**Blocked patterns (console.warn):** Same set (act, uncontrolled, prop leaks, keys).

### Avoiding act() Warnings
For "renders loading state initially" tests: make the primary API mock return a never-resolving promise so state never updates after render:

```jsx
ExamService.getCaso.mockReturnValue(new Promise(() => {})); // never resolves
render(<SomeComponent />);
// test assertions here — no async state updates will occur
```

For tests that trigger state updates (clicks, form submissions): end with `await waitFor(() => {})` to flush pending async effects:

```jsx
import { render, screen, fireEvent, waitFor } from '@testing-library/react';

fireEvent.click(screen.getByText('Submit'));
expect(screen.getByText('Saved')).toBeInTheDocument();
await waitFor(() => {}); // flushes act() from state update
```

### Using `__SKIP_CONSOLE_CHECKS__`
When a test **intentionally** triggers one of the blocked patterns (e.g., testing React's controlled/uncontrolled warning), set the flag before the action that triggers the warning:

```jsx
// Inside your test:
globalThis.__SKIP_CONSOLE_CHECKS__ = true;
// ... perform action that triggers expected warning ...
globalThis.__SKIP_CONSOLE_CHECKS__ = false; // automatically reset in afterEach, but explicit reset is fine
```

This is rarely needed. Prefer fixing the underlying issue over skipping the check.

### CI Enforcement
The GitHub Actions workflow (`.github/workflows/node.js.yml`) runs a post-test grep check that fails CI if any blocked patterns appear in test output. The pre-commit hook (`hooks/pre-commit`) runs the full test suite locally before every commit.

### Expected Console Output
Tests that exercise error paths (e.g., API failures that display error alerts) may log to `console.error` as part of normal behavior. These are **allowed** and do not fail tests — only the specific warning patterns above are blocked.

## Pre-commit Hook
A pre-commit hook runs `npx vitest run` before every commit. To set up on a fresh clone:
```bash
cp hooks/pre-commit .git/hooks/ && chmod +x .git/hooks/pre-commit
```
To bypass temporarily: `git commit --no-verify`
23 changes: 23 additions & 0 deletions hooks/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/bin/sh
# Pre-commit hook to run tests and catch console warnings before pushing
# This file should be symlinked or copied to .git/hooks/pre-commit
#
# Quick setup (from project root):
# mkdir -p .git/hooks && cp hooks/pre-commit .git/hooks/ && chmod +x .git/hooks/pre-commit
#
# Or use Husky for automatic installation (recommended for teams):
# npx husky add .husky/pre-commit 'npx vitest run'

echo "Running tests before commit..."

npx vitest run

if [ $? -ne 0 ]; then
echo ""
echo "ERROR: Tests failed. Please fix all failing tests before committing."
echo "If you need to skip this hook temporarily, use: git commit --no-verify"
exit 1
fi

echo "Tests passed successfully!"
exit 0
1 change: 0 additions & 1 deletion src/components/PlayerCasoContainer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import ExamService from "../services/ExamService";
import { useHistory } from 'react-router-dom';
import { alertError, alertSuccess } from "../services/AlertService";
import CasoContext from "../context/CasoContext";

import EnarmUtil from "../modules/EnarmUtil";
import ContributionTypeSelector from "./ContributionTypeSelector";
import ContributionsSummary from "./ContributionsSummary";
Expand Down
3 changes: 2 additions & 1 deletion src/components/PlayerDashboard.test.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

import { render, screen, fireEvent } from "@testing-library/react";
import { render, screen, fireEvent, waitFor } from "@testing-library/react";
import "@testing-library/jest-dom";
import { vi, describe, beforeEach, it, expect } from 'vitest';
import PlayerDashboard from "./PlayerDashboard";
Expand Down Expand Up @@ -128,5 +128,6 @@ describe("PlayerDashboard Component", () => {

fireEvent.mouseLeave(cardioCard);
expect(cardioCard.className).not.toMatch(/specialtyItemFocused|_specialtyItemFocused_/);
await waitFor(() => {});
});
});
19 changes: 11 additions & 8 deletions src/components/Profile.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -302,14 +302,17 @@ const Profile = () => {
{categories.map(cat => {
const isSelected = selectedSpecialties.includes(cat.id);
return (
<div key={cat.id} className="col s12 m6 l4" style={{ marginBottom: '1rem' }}>
<CustomCheckbox
id={`spec-${cat.id}`}
label={cat.name}
checked={isSelected}
onChange={() => toggleSpecialty(cat.id)}
/>
</div>
<CustomCheckbox
key={cat.id}
id={`spec-${cat.id}`}
label={cat.name}
checked={isSelected}
onChange={() => toggleSpecialty(cat.id)}
s={12}
m={6}
l={4}
style={{ marginBottom: '1rem' }}
/>
);
})}
</div>
Expand Down
37 changes: 18 additions & 19 deletions src/components/admin/AnswerForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,25 +25,24 @@ const AnswerForm = ({

return (
<CustomRow className="answer-wrapper" style={{ marginBottom: '15px', alignItems: 'center' }}>
<CustomCol s={3} m={2}>
<div style={{ marginTop: '20px' }}>
<CustomCheckbox
id={`answer-iscorrect-${keyId}`}
label="¿Correcta?"
checked={answer.is_correct}
onChange={(event) => {
onChangeAnswer(
questionIndex,
answerIndex,
"is_correct",
event
);
}}
name={answerIsCorrectName}
value="true"
/>
</div>
</CustomCol>
<CustomCheckbox
s={3}
m={2}
style={{ marginTop: '20px' }}
id={`answer-iscorrect-${keyId}`}
label="¿Correcta?"
checked={answer.is_correct}
onChange={(event) => {
onChangeAnswer(
questionIndex,
answerIndex,
"is_correct",
event
);
}}
name={answerIsCorrectName}
value="true"
/>
<CustomCol s={8} m={9}>
<CustomTextInput
id={`answer-text-${keyId}`}
Expand Down
1 change: 1 addition & 0 deletions src/components/admin/CasoContainer.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ describe('CasoContainer Component', () => {
});

test('loads data for an existing case and calls Materialize.updateTextFields', async () => {
globalThis.__SKIP_CONSOLE_CHECKS__ = true;
mockParams = { identificador: '1' };
renderContainer();

Expand Down
Loading
Loading