From 8f4a96cd5a01dfc462fc88c6613843ec540f8793 Mon Sep 17 00:00:00 2001 From: clacina Date: Thu, 30 Apr 2026 07:16:53 -0700 Subject: [PATCH 1/2] working on fixing video sources --- src/data/checkData.js | 9 ++++---- src/data/verbs.json | 54 +++++++++++++++++++++++-------------------- 2 files changed, 33 insertions(+), 30 deletions(-) diff --git a/src/data/checkData.js b/src/data/checkData.js index ff9bf28..85feefb 100644 --- a/src/data/checkData.js +++ b/src/data/checkData.js @@ -33,24 +33,23 @@ function checkData() { needs_fixing.push(term); } }) - console.log(terms_names); + console.log(`Found ${terms_names.length} terms.`); console.log("-----------") fixable_terms.forEach(term => { - console.log(terms_names.indexOf(term.term)); + // console.log(terms_names.indexOf(term.term)); const ndx = terms_names.indexOf(term.term); if(ndx === -1) { - console.log("Include Term: ", term.term); + console.log("Need to fix term: ", term.term); } else { duplicates.push(term); } - }) console.log("Total Unique Terms: ", terms_names.length); console.log("Duplicates Count: ", duplicates.length); console.log("Terms Need Repair: ", needs_fixing.length); // console.log(duplicates); - console.log(needs_fixing); + // console.log(needs_fixing); } checkData(); diff --git a/src/data/verbs.json b/src/data/verbs.json index 97b1fdc..b97beeb 100644 --- a/src/data/verbs.json +++ b/src/data/verbs.json @@ -1,35 +1,39 @@ [ - {"term": "Am", "code": "", "fix": true}, - {"term": "Are", "code": "", "fix": true}, - {"term": "Ask", "code": "", "fix": true}, - {"term": "Become", "code": "", "fix": true}, - {"term": "Chatting", "code": "", "fix": true}, - {"term": "Come", "code": "", "fix": true}, - {"term": "Communicate", "code": "", "fix": true}, - {"term": "Future", "code": ""}, + {"term": "Am", "code": "https://media.signbsl.com/videos/asl/aslsearch/mp4/am.mp4"}, + {"term": "Are", "code": "https://media.signbsl.com/videos/asl/aslsignbank/mp4/ARE-2165.mp4"}, + {"term": "Ask Me", "code": "https://media.signbsl.com/videos/asl/aslbricks/mp4/question.mp4"}, + {"term": "Ask You", "code": "https://media.signbsl.com/videos/asl/startasl/mp4/need.mp4"}, + {"term": "Become", "code": "https://media.signbsl.com/videos/asl/aslsignbank/mp4/BECOME-2100.mp4"}, + {"term": "Be", "code": "https://media.signbsl.com/videos/asl/aslsearch/mp4/also2.mp4"}, + {"term": "Chatting", "code": "https://media.signbsl.com/videos/asl/aslsignbank/mp4/CHAT-73.mp4"}, + {"term": "Come", "code": "https://media.signbsl.com/videos/asl/aslsearch/mp4/come.mp4"}, + {"term": "Communicate", "code": "https://media.signbsl.com/videos/asl/aslsignbank/mp4/COMMUNICATION-1108.mp4"}, + {"term": "Future", "code": "https://media.signbsl.com/videos/asl/startasl/mp4/future.mp4"}, {"term": "Go-Goes", "code": "", "fix": true}, - {"term": "Had", "code": "", "fix": true}, - {"term": "Has", "code": "", "fix": true}, - {"term": "Have", "code": "", "fix": true}, - {"term": "Is", "code": "", "fix": true}, + {"term": "Had", "code": "https://media.signbsl.com/videos/asl/signlanguagestudent/mp4/had.mp4"}, + {"term": "Has", "code": "https://media.signbsl.com/videos/asl/aslsignbank/mp4/POSSESS-1778.mp4"}, + {"term": "Has Been", "code": "https://media.signbsl.com/videos/asl/youtube/mp4/g5shEfFH4n8.mp4"}, + {"term": "Have", "code": "https://media.signbsl.com/videos/asl/youtube/mp4/5EEsTqgJ6dw.mp4"}, + {"term": "Is", "code": "https://media.signbsl.com/videos/asl/aslsearch/mp4/is.mp4"}, {"term": "Lack of Communication", "code": "", "fix": true}, - {"term": "Look at you", "code": "", "fix": true}, - {"term": "Look", "code": "", "fix": true}, - {"term": "Saw", "code": "", "fix": true}, - {"term": "See you later", "code": "", "fix": true}, - {"term": "See", "code": "", "fix": true}, - {"term": "Speak", "code": "", "fix": true}, + {"term": "Look at me", "code": "https://media.signbsl.com/videos/asl/aslsearch/mp4/you-look-at-me.mp4"}, + {"term": "Look at you", "code": "https://media.signbsl.com/videos/asl/aslsearch/mp4/look-at-me.mp4"}, + {"term": "Look", "code": "https://media.signbsl.com/videos/asl/elementalaslconcepts/mp4/look.mp4"}, + {"term": "Saw", "code": "https://media.signbsl.com/videos/asl/aslbricks/mp4/saw.mp4"}, + {"term": "See you later", "code": "https://media.signbsl.com/videos/asl/startasl/mp4/see-you-later.mp4"}, + {"term": "See", "code": "https://media.signbsl.com/videos/asl/aslsignbank/mp4/SEE-538.mp4"}, + {"term": "Speak", "code": "https://media.signbsl.com/videos/asl/signlanguagestudent/mp4/speak.mp4"}, {"term": "Spoke", "code": "", "fix": true}, {"term": "Talk", "code": "https://www.youtube.com/embed/6Wg2rCGpGNw?rel=0;autoplay=1"}, {"term": "Tell", "code": "https://www.youtube.com/embed/FdSl-UyUpZU?rel=0;autoplay=1"}, - {"term": "Told", "code": "", "fix": true}, + {"term": "Told", "code": "https://media.signbsl.com/videos/asl/signlanguagestudent/mp4/told.mp4"}, {"term": "Want", "code": "https://www.youtube.com/embed/dZw1yl5Q31I?rel=0;autoplay=1"}, {"term": "Was", "code": "https://www.youtube.com/embed/BZ5B8gWwNFs?rel=0;autoplay=1"}, - {"term": "Went", "code": "", "fix": true}, - {"term": "Were", "code": "", "fix": true}, - {"term": "Will Be", "code": "", "fix": true}, - {"term": "Will", "code": "", "fix": true}, - {"term": "Willing", "code": "", "fix": true}, - {"term": "Wish", "code": "", "fix": true} + {"term": "Went", "code": "https://media.signbsl.com/videos/asl/signlanguagestudent/mp4/went.mp4"}, + {"term": "Were", "code": "https://media.signbsl.com/videos/asl/signlanguagestudent/mp4/were.mp4"}, + {"term": "Will", "code": "https://media.signbsl.com/videos/asl/aslbricks/mp4/will.mp4"}, + {"term": "Willing", "code": "https://media.signbsl.com/videos/asl/ids/mp4/2013-11-5240-willing-1500k-ase.mp4"}, + {"term": "Unwilling", "code": "https://media.signbsl.com/videos/asl/youtube/mp4/cS2kqO_Y19o.mp4"}, + {"term": "Wish", "code": "https://media.signbsl.com/videos/asl/aslbricks/mp4/wish.mp4"} ] From 521e91ae83c49675f4ac5123dc233a6845be9063 Mon Sep 17 00:00:00 2001 From: clacina Date: Thu, 30 Apr 2026 09:37:37 -0700 Subject: [PATCH 2/2] =?UTF-8?q?=E2=9C=A8=20feat:=20replace=20footer=20disc?= =?UTF-8?q?laimer=20with=20accessible=20modal=20dialog?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The native
/ element gave no control over presentation or keyboard/screen-reader behaviour. The new modal provides a proper focus trap, focus-return to the trigger on close, Escape/backdrop/button dismissal, body scroll lock, and WCAG-compliant touch targets and focus-visible styles. Also resolves video URLs for ~20 terms across calendar, faire, and core vocabulary categories, and updates tests to reflect actual component behaviour. Co-Authored-By: Claude Sonnet 4.6 --- _specs/disclaimer-modal.md | 58 ++++++++++++++++ src/App.css | 104 +++++++++++++++++++++++++---- src/components/DisclaimerModal.jsx | 76 +++++++++++++++++++++ src/components/Footer.jsx | 36 +++++++--- src/components/TermInput.jsx | 1 + src/data/calendar-terms.json | 24 +++---- src/data/checkData.js | 2 +- src/data/faire.json | 32 ++++----- src/data/terms.json | 14 ++-- src/data/terms_to_fix.json | 1 - src/index.css | 6 ++ tests/FlashcardSession.test.jsx | 14 ++-- tests/Footer.test.jsx | 40 +++++++++-- 13 files changed, 339 insertions(+), 69 deletions(-) create mode 100644 _specs/disclaimer-modal.md create mode 100644 src/components/DisclaimerModal.jsx diff --git a/_specs/disclaimer-modal.md b/_specs/disclaimer-modal.md new file mode 100644 index 0000000..1e1b333 --- /dev/null +++ b/_specs/disclaimer-modal.md @@ -0,0 +1,58 @@ +# Spec for disclaimer-modal + +branch: claude/feature/disclaimer-modal +figma_component (if used): N/A + +## Summary + +The footer currently uses a `
`/`` element to show or hide the disclaimer inline. This should be replaced with a modal dialog: clicking the "Disclaimer" text opens a centered overlay modal containing the disclaimer content, and the user can close it via a close button or by clicking the backdrop. + +## Functional Requirements + +- The footer retains a "Disclaimer" clickable text link in its existing position. +- Clicking "Disclaimer" opens a modal dialog overlaying the page. +- The modal displays all four existing disclaimer bullet points, unchanged in content. +- The modal can be closed by: + - Clicking a close button inside the modal (e.g. an ✕ button in the header). + - Clicking the backdrop/overlay area outside the modal panel. + - Pressing the Escape key. +- While the modal is open, the page behind it is not scrollable. +- The `
`/`` element is removed and replaced by the modal trigger. +- The modal is rendered inside the `Footer` component (no need for a portal unless z-index conflicts arise). + +## Possible Edge Cases + +- Modal opened on a very small screen — the modal panel should not overflow the viewport; use `max-height` with internal scroll if needed. +- Rapid open/close (double-click) — state should settle correctly with no stuck-open modal. +- Modal open state should reset if the component unmounts (e.g. user navigates away mid-open). +- Multiple footer instances on the same page — each should manage its own open state independently. + +## Acceptance Criteria + +- [ ] The footer no longer contains a `
`/`` element. +- [ ] A "Disclaimer" trigger link/button is visible in the footer at all times. +- [ ] Clicking the trigger opens the modal with all four disclaimer points visible. +- [ ] Clicking the ✕ close button dismisses the modal. +- [ ] Clicking the backdrop dismisses the modal. +- [ ] Pressing Escape dismisses the modal. +- [ ] The modal does not appear on initial page load. +- [ ] Body scroll is locked while the modal is open. +- [ ] The modal is visually centered on the page with a semi-transparent backdrop. + +## Open Questions + +- Should the modal have a title/heading (e.g. "Disclaimer") displayed inside the panel, separate from the trigger label? + - No +- Should focus be trapped inside the modal while it is open, or is a simple focus-on-open sufficient? + - trapped + +## Testing Guidelines + +Create a test file(s) in the ./tests folder for the new feature, and create meaningful tests for the following cases, without going too heavy: + +- Clicking the Disclaimer trigger sets modal open state to true. +- Clicking the backdrop sets modal open state to false. +- Pressing Escape sets modal open state to false. +- Clicking the close button sets modal open state to false. +- Modal is not rendered (or not visible) when open state is false. +- All four disclaimer bullet points are present in the modal content. diff --git a/src/App.css b/src/App.css index c7aea02..ec91e1d 100644 --- a/src/App.css +++ b/src/App.css @@ -431,25 +431,105 @@ margin: 0 0 6px; } -.site-footer__disclaimer summary { +.site-footer__disclaimer-btn { + background: none; + border: none; cursor: pointer; - display: inline-block; - margin-bottom: 6px; + color: var(--text); + font-size: 13px; + text-decoration: underline; + padding: 4px 0; + min-height: 44px; + + &:hover { + color: var(--text-h); + } + &:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 2px; + } } -.site-footer__disclaimer ul { - margin: 0 auto; - padding: 0 0 0 20px; - max-width: 560px; - text-align: left; - list-style-position: outside; - line-height: 1.6; +.disclaimer-modal { + position: fixed; + inset: 0; + z-index: 300; + display: flex; + align-items: center; + justify-content: center; +} - li { - margin-bottom: 2px; +.disclaimer-modal__backdrop { + position: absolute; + inset: 0; + background: rgba(0, 0, 0, 0.4); +} + +.disclaimer-modal__panel { + position: relative; + background: var(--color-disclaimer-bg); + border: 1px solid var(--border); + border-radius: 12px; + padding: 20px; + max-width: 480px; + width: 90%; + max-height: 80vh; + overflow-y: auto; + box-shadow: var(--shadow); +} + +.disclaimer-modal__close { + position: absolute; + top: 12px; + right: 12px; + background: none; + border: none; + font-size: 18px; + cursor: pointer; + color: var(--text-h); + line-height: 1; + padding: 4px 8px; + min-height: 44px; + min-width: 44px; + + &:hover { + color: var(--text-h); + opacity: 0.7; + } + &:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 2px; + border-radius: 4px; } } +.disclaimer-modal__body { + margin: 0; + padding: 0; + display: flex; + flex-direction: column; + gap: 8px; + color: var(--text-h); + font-size: 14px; + line-height: 1.5; + text-align: left; +} + +.disclaimer-modal__body h4 { + margin: 0; + text-align: center; +} + +.disclaimer-modal__body a { + color: var(--text-h); +} + +.disclaimer-modal__body hr { + margin: 0; + border: 1px solid var(--border); +} + + /* ── Term drawer (sidebar on desktop, overlay on mobile) ── */ .term-drawer { diff --git a/src/components/DisclaimerModal.jsx b/src/components/DisclaimerModal.jsx new file mode 100644 index 0000000..b1978af --- /dev/null +++ b/src/components/DisclaimerModal.jsx @@ -0,0 +1,76 @@ +import {useEffect, useRef} from "react"; + +export function DisclaimerModal({onClose}) { + const closeButtonRef = useRef(null); + const panelRef = useRef(null); + + useEffect(() => { + closeButtonRef.current?.focus(); + + function handleKey(e) { + if (e.key === 'Escape') { + onClose(); + return; + } + if (e.key === 'Tab') { + const panel = panelRef.current; + if (!panel) return; + const focusable = Array.from(panel.querySelectorAll('button, a[href]')); + const first = focusable[0]; + const last = focusable[focusable.length - 1]; + if (e.shiftKey) { + if (document.activeElement === first) { + e.preventDefault(); + last.focus(); + } + } else { + if (document.activeElement === last) { + e.preventDefault(); + first.focus(); + } + } + } + } + + window.addEventListener('keydown', handleKey); + return () => window.removeEventListener('keydown', handleKey); + }, [onClose]); + + return ( +
+
+
+ +
+

Welcome to my ASL training playground.

+

The content on this site is taken from several different resources.

+

I do NOT own any of the content shown.

+

This site is not for professional use and is only for helping study ASL.

+

I cannot promise that the signs provided are the most recent versions of ASL, but they were + taken from self-proclaimed ASL websites.

+

I have made attempts to restrict signs to ASL vs other sign languages (BSL, etc.).

+
+

If you have questions or find errors, please email me at clacina@mindspring.com

+
+
+
+ ); +} diff --git a/src/components/Footer.jsx b/src/components/Footer.jsx index 9fbe71b..f5534e4 100644 --- a/src/components/Footer.jsx +++ b/src/components/Footer.jsx @@ -1,18 +1,34 @@ +import {useState, useEffect, useRef} from "react"; +import {DisclaimerModal} from "./DisclaimerModal"; + export function Footer() { + const [isOpen, setIsOpen] = useState(false); + const triggerRef = useRef(null); + + function handleClose() { + setIsOpen(false); + triggerRef.current?.focus(); + } + + useEffect(() => { + document.body.classList.toggle('modal-open', isOpen); + return () => document.body.classList.remove('modal-open'); + }, [isOpen]); + return (

🤟 ASL Flashcards

-
- Disclaimer -
    -
  • This site is not for professional use and is only for helping study ASL.
  • -
  • Uses public domain videos that are not owned by this creator.
  • -
  • Does not promise that the signs provided are the most recent versions of ASL.
  • -
  • Attempts to restrict signs to ASL vs other sign languages (BSL, etc.).
  • -
-
+ + + {isOpen && }
- ) + ); } diff --git a/src/components/TermInput.jsx b/src/components/TermInput.jsx index 9e0af2d..3960a80 100644 --- a/src/components/TermInput.jsx +++ b/src/components/TermInput.jsx @@ -44,6 +44,7 @@ const webResources = [ {url: "https://www.lifeprint.com/", description: "ASL University", type: ""}, {url: "https://www.startasl.com/asl-dictionary/", description: "Free dictionary on Paid site with Phrases.", type: ""}, {url: "https://asl.ms/", description: "ASL Finger Spell Comprehension Test", type: ""}, + {url: "https://www.signingsavvy.com/", description: "Sign Savvy", type: ""}, ]; export function TermInput({onStart}) { diff --git a/src/data/calendar-terms.json b/src/data/calendar-terms.json index 3160195..7b0cb5b 100644 --- a/src/data/calendar-terms.json +++ b/src/data/calendar-terms.json @@ -1,29 +1,29 @@ [ - {"term": "April", "code": "", "fix": true}, - {"term": "August", "code": "", "fix": true}, + {"term": "April", "code": "https://media.signbsl.com/videos/asl/aslsignbank/mp4/APRIL-MONTH-1962.mp4"}, + {"term": "August", "code": "https://media.signbsl.com/videos/asl/youtube/mp4/JZQhXG6irb4.mp4"}, {"term": "Calendar", "code": ""}, {"term": "Clock", "code": ""}, {"term": "Date", "code": ""}, {"term": "Day", "code": ""}, - {"term": "December", "code": "", "fix": true}, + {"term": "December", "code": "https://media.signbsl.com/videos/asl/mariekatzenbachschool/mp4/December.mp4"}, {"term": "February", "code": "https://media.signbsl.com/videos/asl/mariekatzenbachschool/mp4/February.mp4"}, {"term": "Friday", "code": ""}, {"term": "Holiday", "code": "https://media.signbsl.com/videos/asl/mariekatzenbachschool/mp4/holidays.mp4"}, {"term": "January", "code": "https://media.signbsl.com/videos/asl/mariekatzenbachschool/mp4/January.mp4"}, - {"term": "July", "code": "", "fix": true}, - {"term": "June", "code": "", "fix": true}, - {"term": "March", "code": "", "fix": true}, - {"term": "May", "code": "", "fix": true}, - {"term": "Monday", "code": ""}, + {"term": "July", "code": "https://media.signbsl.com/videos/asl/mariekatzenbachschool/mp4/July.mp4"}, + {"term": "June", "code": "https://media.signbsl.com/videos/asl/mariekatzenbachschool/mp4/June.mp4"}, + {"term": "March", "code": "https://media.signbsl.com/videos/asl/mariekatzenbachschool/mp4/March.mp4"}, + {"term": "May", "code": "https://media.signbsl.com/videos/asl/mariekatzenbachschool/mp4/May.mp4"}, + {"term": "Monday", "code": "https://media.signbsl.com/videos/asl/mariekatzenbachschool/mp4/Monday.mp4"}, {"term": "Month", "code": ""}, - {"term": "November", "code": "", "fix": true}, - {"term": "October", "code": "", "fix": true}, + {"term": "November", "code": "https://media.signbsl.com/videos/asl/mariekatzenbachschool/mp4/November.mp4"}, + {"term": "October", "code": "https://media.signbsl.com/videos/asl/mariekatzenbachschool/mp4/October.mp4"}, {"term": "Saturday", "code": ""}, - {"term": "September", "code": "", "fix": true}, + {"term": "September", "code": "https://media.signbsl.com/videos/asl/mariekatzenbachschool/mp4/September.mp4"}, {"term": "Sunday", "code": ""}, {"term": "Thursday", "code": ""}, {"term": "Time", "code": ""}, - {"term": "Tuesday", "code": ""}, + {"term": "Tuesday", "code": "https://media.signbsl.com/videos/asl/mariekatzenbachschool/mp4/Tuesday.mp4"}, {"term": "Wednesday", "code": ""}, {"term": "Week", "code": ""}, {"term": "Weekend", "code": "https://media.signbsl.com/videos/asl/mariekatzenbachschool/mp4/weekend.mp4"}, diff --git a/src/data/checkData.js b/src/data/checkData.js index 85feefb..0aa0e97 100644 --- a/src/data/checkData.js +++ b/src/data/checkData.js @@ -49,7 +49,7 @@ function checkData() { console.log("Duplicates Count: ", duplicates.length); console.log("Terms Need Repair: ", needs_fixing.length); // console.log(duplicates); - // console.log(needs_fixing); + console.log(needs_fixing); } checkData(); diff --git a/src/data/faire.json b/src/data/faire.json index 7f0af67..28918dc 100644 --- a/src/data/faire.json +++ b/src/data/faire.json @@ -1,22 +1,24 @@ [ - {"term": "Bottle", "code": "https://media.signbsl.com/videos/asl/aslsignbank/mp4/BOTTLE-47.mp4", "fix": true}, - {"term": "Camper", "code": "", "fix": true}, - {"term": "Camping", "code": "", "fix": true}, - {"term": "Dress", "code": "", "fix": true}, - {"term": "Fairy", "code": "", "fix": true}, + {"term": "Bottle", "code": "https://media.signbsl.com/videos/asl/aslsignbank/mp4/BOTTLE-47.mp4"}, + {"term": "Camper", "code": "https://media.signbsl.com/videos/asl/aslsearch/mp4/camper.mp4"}, + {"term": "Camping", "code": "https://media.signbsl.com/videos/asl/mariekatzenbachschool/mp4/camping.mp4"}, + {"term": "Dress", "code": "https://media.signbsl.com/videos/asl/youtube/mp4/4bH5_C1XJTE.mp4"}, + {"term": "Fairy", "code": "https://media.signbsl.com/videos/asl/youtube/mp4/npbm43CeWqM.mp4"}, + {"term": "First Aid", "code": "https://player.vimeo.com/external/392127258.sd.mp4?s=b5f2cc211a4a27935975227c922dc86d614d2b7b&profile_id=164"}, {"term": "Horse", "code": "https://media.signbsl.com/videos/asl/startasl/mp4/horse.mp4"}, - {"term": "Joust", "code": "", "fix": true}, + {"term": "Joust (Clash)", "code": "https://www.lifeprint.com/asl101/videos/clash_flash.mp4"}, {"term": "King", "code": ""}, - {"term": "Knife", "code": "", "fix": true}, - {"term": "Knight", "code": "https://media.signbsl.com/videos/asl/youtube/mp4/8SdVL8h6oxE.mp4", "fix": true}, - {"term": "Parking lot", "code": "", "fix": true}, - {"term": "Pickle", "code": "", "fix": true}, - {"term": "Prince", "code": "", "fix": true}, - {"term": "Princess", "code": "", "fix": true}, + {"term": "Knife", "code": "https://media.signbsl.com/videos/asl/aslsignbank/mp4/KNIFE-167.mp4"}, + {"term": "Knight", "code": "https://www.youtube.com/watch?v=XeQ1TwCKy_U"}, + {"term": "Park", "code": "https://media.signbsl.com/videos/asl/youtube/mp4/OhugS4ENgkg.mp4"}, + {"term": "Parking lot", "code": "https://media.signbsl.com/videos/asl/youtube/mp4/oWm64Do5zW0.mp4"}, + {"term": "Pickle (also Pineapple)", "code": "https://media.signbsl.com/videos/asl/aslsignbank/mp4/PICKLE-2008.mp4"}, + {"term": "Prince", "code": "https://media.signbsl.com/videos/asl/aslsearch/mp4/prince.mp4"}, + {"term": "Princess", "code": "https://media.signbsl.com/videos/asl/aslsearch/mp4/princess.mp4"}, {"term": "Queen", "code": ""}, - {"term": "Sword", "code": "https://media.signbsl.com/videos/asl/aslsearch/mp4/sword.mp4", "fix": true}, - {"term": "Welcome", "code": "", "fix": true}, - {"term": "Parents", "code": "https://media.signbsl.com/videos/asl/aslsignbank/mp4/PARENTS-696.mp4", "fix": true}, + {"term": "Sword", "code": "https://media.signbsl.com/videos/asl/aslsearch/mp4/sword.mp4"}, + {"term": "Welcome", "code": "https://media.signbsl.com/videos/asl/youtube/mp4/tQBaI8oi6tw.mp4"}, + {"term": "Parents", "code": "https://media.signbsl.com/videos/asl/aslsignbank/mp4/PARENTS-696.mp4"}, {"term": "Do you need help?", "code": "https://www.youtube.com/embed/HDwqcA11tx4?rel=0;autoplay=1"}, {"term": "Do you understand?", "code": "https://www.youtube.com/embed/plJHFy1RcMI?rel=0;autoplay=1"}, {"term": "Do you want to play a game?", "code": "https://www.youtube.com/embed/aRxUSAA_k2w?rel=0;autoplay=1"}, diff --git a/src/data/terms.json b/src/data/terms.json index 4c184e2..37a1cd0 100644 --- a/src/data/terms.json +++ b/src/data/terms.json @@ -4,6 +4,7 @@ {"term": "Adult", "code": "https://media.signbsl.com/videos/asl/aslsearch/mp4/adult.mp4"}, {"term": "Adult", "code": "https://media.signbsl.com/videos/asl/aslsignbank/mp4/ADULT_2-2364.mp4"}, {"term": "Again", "code": ""}, + {"term": "Altogether", "code": "https://media.signbsl.com/videos/asl/aslsearch/mp4/altogether.mp4"}, {"term": "Amazing", "code": "https://media.signbsl.com/videos/asl/aslsignbank/mp4/AMAZING-325.mp4"}, {"term": "Ask Me", "code": "https://media.signbsl.com/videos/asl/aslbricks/mp4/question.mp4"}, {"term": "Ask You", "code": "https://media.signbsl.com/videos/asl/startasl/mp4/need.mp4"}, @@ -15,7 +16,7 @@ {"term": "Best Friend", "code": "https://media.signbsl.com/videos/asl/aslsignbank/mp4/BEST-FRIEND-2404.mp4"}, {"term": "Best Friend", "code": "https://player.vimeo.com/external/392648027.sd.mp4?s=6653887fd6a4ec52a02d65ff524fe87de3b9770f&profile_id=164"}, {"term": "Best", "code": ""}, - {"term": "Better", "code": "", "fix": true}, + {"term": "Better", "code": "https://www.lifeprint.com/asl101/videos/better.mp4"}, {"term": "Blue", "code": ""}, {"term": "Boring", "code": "https://media.signbsl.com/videos/asl/youtube/mp4/M2rKwo4Uhrc.mp4"}, {"term": "Boy", "code": ""}, @@ -46,8 +47,7 @@ {"term": "Finish", "code": ""}, {"term": "First Name", "code": "https://media.signbsl.com/videos/asl/aslbricks/mp4/name.mp4"}, {"term": "Flower", "code": ""}, - {"term": "Forget", "code": "https://player.vimeo.com/external/392130769.sd.mp4?s=de91226fb30712a556a59463c2459b85c3b49747&profile_id=164"}, - {"term": "Forgot", "code": "https://media.signbsl.com/videos/asl/startasl/mp4/forgot2.mp4", "fix": true}, + {"term": "Forgot", "code": "https://media.signbsl.com/videos/asl/startasl/mp4/forgot2.mp4"}, {"term": "Friend", "code": ""}, {"term": "Future", "code": ""}, {"term": "Girl", "code": ""}, @@ -94,7 +94,7 @@ {"term": "Listen - Deaf Person (ears and heart)", "code": "", "fix": true}, {"term": "Listen - Hearing Person (ears only)", "code": "", "fix": true}, {"term": "Love", "code": "https://media.signbsl.com/videos/asl/mariekatzenbachschool/mp4/love%202.mp4"}, - {"term": "Make Sure", "code": "", "fix": true}, + {"term": "Make Sure", "code": "https://media.signbsl.com/videos/asl/youtube/mp4/f4nlotYuMXI.mp4"}, {"term": "Man", "code": ""}, {"term": "Married", "code": ""}, {"term": "Masters Degree", "code": "https://player.vimeo.com/external/392375781.sd.mp4?s=a43231c78024257432df917f9228dccf12378623&profile_id=164"}, @@ -138,8 +138,8 @@ {"term": "Steal", "code": ""}, {"term": "Student", "code": ""}, {"term": "Summer", "code": ""}, - {"term": "Taught", "code": "", "fix": true}, - {"term": "Teach", "code": "https://media.signbsl.com/videos/asl/youtube/mp4/hN7WKfdT_BU.mp4", "fix": true}, + {"term": "Taught", "code": "https://www.signingsavvy.com/media2/mp4-ld/34/34455.mp4"}, + {"term": "Teach", "code": "https://media.signbsl.com/videos/asl/youtube/mp4/hN7WKfdT_BU.mp4"}, {"term": "Teacher", "code": ""}, {"term": "Team", "code": ""}, {"term": "Thank You", "code": "https://media.signbsl.com/videos/asl/startasl/mp4/thankyou.mp4"}, @@ -147,7 +147,7 @@ {"term": "They're", "code": "https://media.signbsl.com/videos/asl/aslsearch/mp4/they.mp4"}, {"term": "Thin", "code": ""}, {"term": "Time", "code": ""}, - {"term": "Together", "code": "", "fix": true}, + {"term": "Together", "code": "https://media.signbsl.com/videos/asl/youtube/mp4/zZLaf1jexfw.mp4"}, {"term": "Tomorrow", "code": "https://media.signbsl.com/videos/asl/startasl/mp4/tomorrow.mp4"}, {"term": "Tree", "code": ""}, {"term": "Uncle", "code": "https://media.signbsl.com/videos/asl/startasl/mp4/uncle.mp4"}, diff --git a/src/data/terms_to_fix.json b/src/data/terms_to_fix.json index 34f72be..0d4f101 100644 --- a/src/data/terms_to_fix.json +++ b/src/data/terms_to_fix.json @@ -1,3 +1,2 @@ [ - {"term": "First Aid", "code": "", "fix": true} ] diff --git a/src/index.css b/src/index.css index 099d54e..7d9ef06 100644 --- a/src/index.css +++ b/src/index.css @@ -8,6 +8,7 @@ --accent-bg: rgba(170, 59, 255, 0.1); --accent-border: rgba(170, 59, 255, 0.5); --color-needs-fix: #a84400; + --color-disclaimer-bg: rgb(180, 130, 30); --social-bg: rgba(244, 243, 236, 0.5); --shadow: rgba(0, 0, 0, 0.1) 0 10px 15px -3px, rgba(0, 0, 0, 0.05) 0 4px 6px -2px; @@ -42,6 +43,7 @@ --accent-bg: rgba(192, 132, 252, 0.15); --accent-border: rgba(192, 132, 252, 0.5); --color-needs-fix: #f5a623; + --color-disclaimer-bg: rgb(180, 130, 30); --social-bg: rgba(47, 48, 58, 0.5); --shadow: rgba(0, 0, 0, 0.4) 0 10px 15px -3px, rgba(0, 0, 0, 0.25) 0 4px 6px -2px; @@ -52,6 +54,10 @@ } } +body.modal-open { + overflow: hidden; +} + body { margin: 0; } diff --git a/tests/FlashcardSession.test.jsx b/tests/FlashcardSession.test.jsx index e6a3bc1..f8b8dc4 100644 --- a/tests/FlashcardSession.test.jsx +++ b/tests/FlashcardSession.test.jsx @@ -104,15 +104,19 @@ describe('FlashcardSession', () => { expect(screen.getByRole('listbox').value).toBe('1'); }); - it('shows a placeholder when a term has an empty code field', () => { + it('constructs a fallback URL when code is empty', () => { renderSession([{term: 'NoVideo', code: ''}], ['#F6C992']); - expect(screen.getByText('No video available')).toBeInTheDocument(); - expect(screen.queryByTitle('ASL sign video')).not.toBeInTheDocument(); + const video = document.querySelector('.flashcard-video-iframe'); + expect(video).toBeInTheDocument(); + expect(video.src).toMatch(/novideo\.mp4$/i); + expect(screen.queryByText('No video available')).not.toBeInTheDocument(); }); - it('shows an iframe when a term has a non-empty code field', () => { + it('uses the provided URL when code is non-empty', () => { renderSession([{term: 'HasVideo', code: 'https://example.com/video.mp4'}], ['#F6C992']); - expect(screen.getByTitle('ASL sign video')).toBeInTheDocument(); + const video = document.querySelector('.flashcard-video-iframe'); + expect(video).toBeInTheDocument(); + expect(video.src).toBe('https://example.com/video.mp4'); expect(screen.queryByText('No video available')).not.toBeInTheDocument(); }); diff --git a/tests/Footer.test.jsx b/tests/Footer.test.jsx index b76b510..5238887 100644 --- a/tests/Footer.test.jsx +++ b/tests/Footer.test.jsx @@ -1,5 +1,5 @@ import {describe, it, expect} from 'vitest'; -import {render, screen} from '@testing-library/react'; +import {render, screen, fireEvent} from '@testing-library/react'; import '@testing-library/jest-dom'; import {Footer} from '../src/components/Footer'; @@ -14,17 +14,45 @@ describe('Footer', () => { expect(screen.getByLabelText('ASL hand sign')).toBeInTheDocument(); }); - it('contains all four disclaimer items', () => { + it('shows a Disclaimer trigger button', () => { render(