Skip to content

hs1 demo: albany template + codex improvements to core code#1128

Open
bryanreed wants to merge 3 commits intocomponent-genfrom
hs1demo
Open

hs1 demo: albany template + codex improvements to core code#1128
bryanreed wants to merge 3 commits intocomponent-genfrom
hs1demo

Conversation

@bryanreed
Copy link
Copy Markdown

Summary

  • add the HS1 Albany starter registry and default layout
  • improve preview/editor behavior for cleaner template comparison flows
  • fix shared hours field resolution so KG bindings and manual hours values both work

Verification

  • corepack pnpm exec vitest run src/utils/resolveComponentData.test.tsx
  • corepack pnpm run build:components
  • corepack pnpm run build:plugin
  • corepack pnpm run typecheck

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 27, 2026

⚠️ Deleted Translation Keys Detected

🔤 Deleted Translation Keys

atms

Key Languages Removed
atms cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW

events

Key Languages Removed
events cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW

failedToPasteComponentPermissionDenied

Key Languages Removed
failedToPasteComponentPermissionDenied cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW

failedToPasteLayoutPermissionDenied

Key Languages Removed
failedToPasteLayoutPermissionDenied cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW

fields

Key Languages Removed
fields.currentPageLinkLabel cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.directoryListBackgroundColor cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.headingBackgroundColor cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.normalizeLink cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.showCountry cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.showCurrentPagesLinkLabel cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW
fields.showRegion cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW

kilometer_zero

Key Languages Removed
kilometer_zero cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt nb,nl pl,pt ro,sk sv,tr zh,zh-TW

mile_zero

Key Languages Removed
mile_zero cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt nb,nl pl,pt ro,sk sv,tr zh,zh-TW

providerFacilities

Key Languages Removed
providerFacilities cs,da de,en en-GB,es et,fi fr,hr hu,it ja,lt lv,nb nl,pl pt,ro sk,sv tr,zh zh-TW

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 27, 2026

Walkthrough

This pull request adds support for hours field configuration in the visual editor and implements a complete HS1 Albany template with eleven section components. It includes the new HOURS_CONSTANT_CONFIG mapping for hours field types, improves null safety in internal editor components through optional chaining, relaxes type validation to allow partial YextEntityField objects, and registers all HS1 Albany section components (Header, Hero, Services, Welcome, Signup Form, Testimonials, Hours, Location, Contact Form, Footer, and Copyright) along with their layout configuration. Additionally, it refines preview mode initialization logic in the development template to properly handle mounting state.

Possibly related PRs

Suggested labels

create-dev-release

Suggested reviewers

  • mkilpatrick
  • briantstephan
  • benlife5
  • asanehisa
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main changes: adding the HS1 Albany template and making improvements to core code for hours field resolution and preview/editor behavior.
Description check ✅ Passed The description is directly related to the changeset, outlining the three main objectives (HS1 Albany registry, preview/editor improvements, hours field fixes) and providing verification steps.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch hs1demo

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/visual-editor/src/utils/resolveComponentData.tsx (1)

275-280: ⚠️ Potential issue | 🟠 Major

Don’t key YextEntityField detection off generic field/constantValue names alone.

With this guard, any literal object that happens to expose a root field or constantValue property is now treated as a binding and routed through resolveYextEntityField(). That means inputs like { field: "literal" } will resolve as KG lookups/undefined instead of staying literal data. Please use an explicit discriminator for YextEntityField payloads, or normalize saved field configs so they always carry one before broadening this check.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/visual-editor/src/utils/resolveComponentData.tsx` around lines 275 -
280, The type guard isYextEntityField incorrectly treats any object with a
top-level "field" or "constantValue" as a YextEntityField, causing plain
literals like { field: "literal" } to be routed through resolveYextEntityField;
update the guard to require a clear discriminator or a stricter shape before
treating something as a YextEntityField (for example, check for a dedicated
discriminator property or verify that "field" is an object with the expected
keys/type), and then use that stricter guard in resolveComponentData where
resolveYextEntityField is called so only genuine YextEntityField payloads are
resolved via resolveYextEntityField.
🧹 Nitpick comments (2)
starter/src/templates/dev.tsx (1)

282-296: Consider keeping a Back to Editor action in preview mode.

openPreview() still does same-tab navigation on Line 273, so once this branch hides the toolbar there is no in-app way back to the editor. A single back action would keep the preview clean without forcing users to rely on browser chrome.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@starter/src/templates/dev.tsx` around lines 282 - 296, The preview branch
hides the toolbar and leaves no in-app way back to the editor; add a "Back to
Editor" action when isPreviewMode is true that restores the editor view instead
of relying on browser chrome. Update the component to render a Back button in
the preview branch which calls a handler that either toggles the same preview
state (e.g., call setIsPreviewMode(false) or a new closePreview function) or
navigates programmatically back to the editor (matching how openPreview()
works), ensuring the handler name (closePreview or setIsPreviewMode) is used
consistently with openPreview and isPreviewMode.
starter/src/registry/hs1-albany/components/Hs1AlbanyHeaderSection.tsx (1)

124-133: Consider using unique keys for array items.

Using ${item.label}-${item.link} as keys could cause collisions if duplicate label/link combinations exist. While unlikely in practice, using the array index as a fallback or a unique ID would be more robust.

💡 Optional: Add index to ensure unique keys
-            {props.primaryLinks.map((item) => (
-              <li key={`${item.label}-${item.link}`}>
+            {props.primaryLinks.map((item, index) => (
+              <li key={`${item.label}-${item.link}-${index}`}>

Apply similarly to moreLinks (line 143) and patientEducation.items (line 163).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@starter/src/registry/hs1-albany/components/Hs1AlbanyHeaderSection.tsx` around
lines 124 - 133, The list item keys in the Hs1AlbanyHeaderSection component
(inside props.primaryLinks.map) use `${item.label}-${item.link}` which can
collide; update the key generation to ensure uniqueness by falling back to the
array index or a unique id when label+link is not unique (apply the same fix for
moreLinks mapping and patientEducation.items mapping), e.g., prefer a unique
identifier on the item (id) and if absent append the map index to the key so
every <li> key is guaranteed unique.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@starter/src/registry/hs1-albany/.captured-artifact/plan.md`:
- Around line 148-162: The checklist lists generated components and layout as
unfinished, which is misleading; either mark each completed item
(Hs1AlbanyHeaderSection, Hs1AlbanyHeroSection, Hs1AlbanyServicesSection,
Hs1AlbanyWelcomeSection, Hs1AlbanySignupFormSection,
Hs1AlbanyTestimonialsSection, Hs1AlbanyHoursSection, Hs1AlbanyLocationSection,
Hs1AlbanyContactFormSection, Hs1AlbanyFooterSection, Hs1AlbanyCopyrightSection,
registration in ve.config.tsx, and creation of defaultLayout.json) as done, or
replace the TODO checklist with a plain reference list of the added components
and artifacts so the file no longer implies work remains.

In `@starter/src/registry/hs1-albany/components/Hs1AlbanyContactFormSection.tsx`:
- Around line 90-123: The form in Hs1AlbanyContactFormSection.tsx is
non-functional and inaccessible: change the button from type="button" to
type="submit", add an onSubmit handler on the form (e.g., handleContactSubmit)
that prevents default, validates fields, and sends data (fetch/axios) or calls a
provided prop; also give each input and the textarea name and required
attributes and add visible <label> elements or aria-label attributes (or
associate labels via htmlFor/id) so the fields are accessible; alternatively, if
no submission is intended, convert the inputs/button into non-interactive text
or disable them and update copy to indicate the form is not functional. Ensure
you edit the form element, the three <input> fields, the <textarea>, and the
Submit <button> in Hs1AlbanyContactFormSection.tsx accordingly.

In `@starter/src/registry/hs1-albany/components/Hs1AlbanyFooterSection.tsx`:
- Around line 106-115: In Hs1AlbanyFooterSection, social link buttons rendered
from props.socialLinks currently only output SVG icons (Icon) inside Link,
making them inaccessible; update the Link to include an accessible name by
adding an aria-label={item.label} (or include visually-hidden text with
item.label) on the Link component so screen readers receive the label for each
Icon, keeping the existing key and cta props intact.

In `@starter/src/registry/hs1-albany/components/Hs1AlbanyHeroSection.tsx`:
- Around line 138-144: The code computes resolvedHeroImage via
resolveComponentData(props.heroImage, locale, streamDocument) but immediately
discards it and always uses capturedHeroImageUrl; replace that by using the
resolved asset's url (e.g., heroImageUrl = (resolvedHeroImage as
TranslatableAssetImage)?.url ?? capturedHeroImageUrl) and remove the void
resolvedHeroImage; ensure you reference resolvedHeroImage and fall back to
capturedHeroImageUrl so entity-selected images render correctly.

In `@starter/src/registry/hs1-albany/components/Hs1AlbanyHoursSection.tsx`:
- Around line 255-263: The desktop rendering in Hs1AlbanyHoursSection.tsx
currently splits item.hours on every " - ", which breaks multi-interval strings
from formatHoursForDay; update the conditional so if item.hours contains a comma
(", ") you render item.hours as-is, otherwise split only the first " - "
occurrence (e.g., find the firstIndexOf " - " and use substring before/after)
when constructing the two parts; reference the item.hours usage in the JSX and
adjust the split logic to use indexOf/substring (or a single split with a limit)
instead of splitting on all " - "s.

In `@starter/src/registry/hs1-albany/components/Hs1AlbanyLocationSection.tsx`:
- Around line 133-139: The distance badge in Hs1AlbanyLocationSection currently
renders a hardcoded "--", so either wire it to the real distance value or hide
the badge when no distance exists: locate the Hs1AlbanyLocationSection component
and replace the static "--" text with the actual distance variable (e.g.,
props.distance or computed formattedDistance) and ensure you format it
(round/trim) and append "mi"; alternatively, wrap the entire <div
className="mb-1 flex items-end gap-1"> block in a conditional render (e.g., only
render when distance != null/undefined) so the placeholder never appears if the
data is absent.

In `@starter/src/registry/hs1-albany/components/Hs1AlbanySignupFormSection.tsx`:
- Around line 91-106: The form in Hs1AlbanySignupFormSection uses a plain <form>
with inputs that only have placeholders and a button set to type="button", so it
never submits and lacks accessible labels; change the button in this component
to type="submit" and add an onSubmit handler (e.g., handleSignupSubmit) on the
<form> to process/validate the name/email, or if this is purely demo chrome,
replace the <form> with a non-interactive container (or explicitly mark it as
mock) and keep the CTA as a non-submitting element; additionally, add proper
<label> elements or visible/aria-label attributes for the two inputs
(name/email) and wire them to state or form refs so the submit handler can read
values.

In `@starter/src/registry/hs1-albany/components/Hs1AlbanyTestimonialsSection.tsx`:
- Around line 104-133: The component currently only uses props.quotes[0]
(activeQuote) and always renders a single <p>, so additional testimonials and
per-quote typography are ignored; update Hs1AlbanyTestimonialsSection to iterate
over props.quotes (e.g., map) and for each quote call
resolveComponentData(quote.quote.text, locale, streamDocument) to compute
quoteText, render each testimonial block (including the quote mark, text, and
any per-quote typography props from the quote item) instead of the hard-coded
single <p>, and remove or repurpose the activeQuote variable if you keep a
single-view mode; ensure you reference props.quotes, resolveComponentData, and
any per-quote typography fields when implementing the mapping.

In `@starter/src/registry/hs1-albany/components/Hs1AlbanyWelcomeSection.tsx`:
- Around line 146-160: The subtitle and body render blocks in
Hs1AlbanyWelcomeSection are using fixed classes/styles so the passed-in
StyledTextProps (subtitle and body) fontSize, fontColor, fontWeight, and
textTransform are ignored; update the subtitle and body render logic to consume
those props: compute style entries (fontSize -> fontSize, fontColor -> color,
fontWeight -> fontWeight, textTransform -> textTransform, plus existing
fontFamily/lineHeight where applicable) and merge them into the inline style
object, and/or compute className fragments from those prop values instead of
hardcoded text sizes/colors; target the JSX elements that reference subtitle and
body in Hs1AlbanyWelcomeSection.tsx and replace the fixed className/style usage
so the StyledTextProps values are applied at render time.

---

Outside diff comments:
In `@packages/visual-editor/src/utils/resolveComponentData.tsx`:
- Around line 275-280: The type guard isYextEntityField incorrectly treats any
object with a top-level "field" or "constantValue" as a YextEntityField, causing
plain literals like { field: "literal" } to be routed through
resolveYextEntityField; update the guard to require a clear discriminator or a
stricter shape before treating something as a YextEntityField (for example,
check for a dedicated discriminator property or verify that "field" is an object
with the expected keys/type), and then use that stricter guard in
resolveComponentData where resolveYextEntityField is called so only genuine
YextEntityField payloads are resolved via resolveYextEntityField.

---

Nitpick comments:
In `@starter/src/registry/hs1-albany/components/Hs1AlbanyHeaderSection.tsx`:
- Around line 124-133: The list item keys in the Hs1AlbanyHeaderSection
component (inside props.primaryLinks.map) use `${item.label}-${item.link}` which
can collide; update the key generation to ensure uniqueness by falling back to
the array index or a unique id when label+link is not unique (apply the same fix
for moreLinks mapping and patientEducation.items mapping), e.g., prefer a unique
identifier on the item (id) and if absent append the map index to the key so
every <li> key is guaranteed unique.

In `@starter/src/templates/dev.tsx`:
- Around line 282-296: The preview branch hides the toolbar and leaves no in-app
way back to the editor; add a "Back to Editor" action when isPreviewMode is true
that restores the editor view instead of relying on browser chrome. Update the
component to render a Back button in the preview branch which calls a handler
that either toggles the same preview state (e.g., call setIsPreviewMode(false)
or a new closePreview function) or navigates programmatically back to the editor
(matching how openPreview() works), ensuring the handler name (closePreview or
setIsPreviewMode) is used consistently with openPreview and isPreviewMode.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 5cd46e32-dd2d-417e-a1ee-53d35a263d57

📥 Commits

Reviewing files that changed from the base of the PR and between 31acea7 and d2baeef.

⛔ Files ignored due to path filters (5)
  • starter/src/registry/hs1-albany/.captured-artifact/rendered-preview.diff.png is excluded by !**/*.png
  • starter/src/registry/hs1-albany/.captured-artifact/rendered-preview.png is excluded by !**/*.png
  • starter/src/registry/hs1-albany/.captured-artifact/screenshot.png is excluded by !**/*.png
  • starter/src/registry/hs1-albany/assets/hero-crop.png is excluded by !**/*.png
  • starter/src/registry/hs1-albany/assets/map-crop.png is excluded by !**/*.png
📒 Files selected for processing (25)
  • packages/visual-editor/src/editor/YextEntityFieldSelector.tsx
  • packages/visual-editor/src/internal/components/InternalLayoutEditor.tsx
  • packages/visual-editor/src/internal/puck/constant-value-fields/Hours.tsx
  • packages/visual-editor/src/internal/puck/ui/UIButtonsToggle.tsx
  • packages/visual-editor/src/utils/resolveComponentData.test.tsx
  • packages/visual-editor/src/utils/resolveComponentData.tsx
  • packages/visual-editor/src/utils/resolveYextEntityField.ts
  • starter/src/registry/hs1-albany/.captured-artifact/combined.css
  • starter/src/registry/hs1-albany/.captured-artifact/manifest.json
  • starter/src/registry/hs1-albany/.captured-artifact/page.html
  • starter/src/registry/hs1-albany/.captured-artifact/plan.md
  • starter/src/registry/hs1-albany/components/Hs1AlbanyContactFormSection.tsx
  • starter/src/registry/hs1-albany/components/Hs1AlbanyCopyrightSection.tsx
  • starter/src/registry/hs1-albany/components/Hs1AlbanyFooterSection.tsx
  • starter/src/registry/hs1-albany/components/Hs1AlbanyHeaderSection.tsx
  • starter/src/registry/hs1-albany/components/Hs1AlbanyHeroSection.tsx
  • starter/src/registry/hs1-albany/components/Hs1AlbanyHoursSection.tsx
  • starter/src/registry/hs1-albany/components/Hs1AlbanyLocationSection.tsx
  • starter/src/registry/hs1-albany/components/Hs1AlbanyServicesSection.tsx
  • starter/src/registry/hs1-albany/components/Hs1AlbanySignupFormSection.tsx
  • starter/src/registry/hs1-albany/components/Hs1AlbanyTestimonialsSection.tsx
  • starter/src/registry/hs1-albany/components/Hs1AlbanyWelcomeSection.tsx
  • starter/src/registry/hs1-albany/defaultLayout.json
  • starter/src/templates/dev.tsx
  • starter/src/ve.config.tsx

Comment on lines +148 to +162
## Implementation Checklist

- [ ] `Hs1AlbanyHeaderSection` -> `components/Hs1AlbanyHeaderSection.tsx`
- [ ] `Hs1AlbanyHeroSection` -> `components/Hs1AlbanyHeroSection.tsx`
- [ ] `Hs1AlbanyServicesSection` -> `components/Hs1AlbanyServicesSection.tsx`
- [ ] `Hs1AlbanyWelcomeSection` -> `components/Hs1AlbanyWelcomeSection.tsx`
- [ ] `Hs1AlbanySignupFormSection` -> `components/Hs1AlbanySignupFormSection.tsx`
- [ ] `Hs1AlbanyTestimonialsSection` -> `components/Hs1AlbanyTestimonialsSection.tsx`
- [ ] `Hs1AlbanyHoursSection` -> `components/Hs1AlbanyHoursSection.tsx`
- [ ] `Hs1AlbanyLocationSection` -> `components/Hs1AlbanyLocationSection.tsx`
- [ ] `Hs1AlbanyContactFormSection` -> `components/Hs1AlbanyContactFormSection.tsx`
- [ ] `Hs1AlbanyFooterSection` -> `components/Hs1AlbanyFooterSection.tsx`
- [ ] `Hs1AlbanyCopyrightSection` -> `components/Hs1AlbanyCopyrightSection.tsx`
- [ ] Register all generated components in `starter/src/ve.config.tsx`
- [ ] Generate `starter/src/registry/hs1-albany/defaultLayout.json` in final visual order
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

This checklist will be stale as soon as this PR merges.

The PR already adds these components and the default layout, so committing this section with every box unchecked leaves misleading guidance in the repo. Please either mark the completed items or rewrite this as a reference list instead of a TODO list.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@starter/src/registry/hs1-albany/.captured-artifact/plan.md` around lines 148
- 162, The checklist lists generated components and layout as unfinished, which
is misleading; either mark each completed item (Hs1AlbanyHeaderSection,
Hs1AlbanyHeroSection, Hs1AlbanyServicesSection, Hs1AlbanyWelcomeSection,
Hs1AlbanySignupFormSection, Hs1AlbanyTestimonialsSection, Hs1AlbanyHoursSection,
Hs1AlbanyLocationSection, Hs1AlbanyContactFormSection, Hs1AlbanyFooterSection,
Hs1AlbanyCopyrightSection, registration in ve.config.tsx, and creation of
defaultLayout.json) as done, or replace the TODO checklist with a plain
reference list of the added components and artifacts so the file no longer
implies work remains.

Comment on lines +90 to +123
<form className="grid gap-4 md:grid-cols-[minmax(0,1fr)_minmax(0,1fr)]">
<div className="space-y-4">
<input
className="h-[44px] w-full border border-white/80 bg-transparent px-3 text-[13px] text-white placeholder:text-white/80"
placeholder="Enter your name (Required)"
/>
<input
className="h-[44px] w-full border border-white/80 bg-transparent px-3 text-[13px] text-white placeholder:text-white/80"
placeholder="Enter email (Required)"
/>
<input
className="h-[44px] w-full border border-white/80 bg-transparent px-3 text-[13px] text-white placeholder:text-white/80"
placeholder="(XXX)XXX-XXXX (Required)"
/>
</div>
<textarea
className="min-h-[164px] w-full border border-white/80 bg-transparent px-3 py-3 text-[13px] text-white placeholder:text-white/80"
placeholder="Notes to the Doctor"
/>
</form>
<p
className="mb-0 mt-5 text-center text-[12px] text-white"
style={{ fontFamily: "Montserrat, Open Sans, sans-serif" }}
>
Please do not submit any Protected Health Information (PHI).
</p>
<div className="mt-5 text-center">
<button
type="button"
className="inline-flex h-[44px] min-w-[220px] items-center justify-center border border-white/80 px-8 text-[13px] font-bold uppercase tracking-[0.08em] text-white"
style={{ fontFamily: "Nunito Sans, Open Sans, sans-serif" }}
>
Submit
</button>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

This section renders a dead form.

type="button" plus no form action/onSubmit means users cannot actually send anything. The placeholder-only fields also aren't sufficient labels, so the UI is inaccessible if it ships as a real form. Either wire a real submission flow or make the section clearly non-interactive.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@starter/src/registry/hs1-albany/components/Hs1AlbanyContactFormSection.tsx`
around lines 90 - 123, The form in Hs1AlbanyContactFormSection.tsx is
non-functional and inaccessible: change the button from type="button" to
type="submit", add an onSubmit handler on the form (e.g., handleContactSubmit)
that prevents default, validates fields, and sends data (fetch/axios) or calls a
provided prop; also give each input and the textarea name and required
attributes and add visible <label> elements or aria-label attributes (or
associate labels via htmlFor/id) so the fields are accessible; alternatively, if
no submission is intended, convert the inputs/button into non-interactive text
or disable them and update copy to indicate the form is not functional. Ensure
you edit the form element, the three <input> fields, the <textarea>, and the
Submit <button> in Hs1AlbanyContactFormSection.tsx accordingly.

Comment on lines +106 to +115
{props.socialLinks.map((item) => {
const Icon = iconMap[item.icon];
return (
<Link
key={`${item.label}-${item.link}`}
cta={{ link: item.link, linkType: "URL" }}
className="flex h-9 w-9 items-center justify-center rounded-full bg-[#dcb65f] text-white no-underline"
>
<Icon />
</Link>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Icon-only social links need an accessible name.

These links render only SVGs, so assistive tech gets unlabeled controls. Add hidden text or an aria-label from item.label.

Suggested fix
               <Link
                 key={`${item.label}-${item.link}`}
                 cta={{ link: item.link, linkType: "URL" }}
                 className="flex h-9 w-9 items-center justify-center rounded-full bg-[`#dcb65f`] text-white no-underline"
               >
-                <Icon />
+                <Icon aria-hidden="true" />
+                <span className="sr-only">{item.label}</span>
               </Link>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{props.socialLinks.map((item) => {
const Icon = iconMap[item.icon];
return (
<Link
key={`${item.label}-${item.link}`}
cta={{ link: item.link, linkType: "URL" }}
className="flex h-9 w-9 items-center justify-center rounded-full bg-[#dcb65f] text-white no-underline"
>
<Icon />
</Link>
{props.socialLinks.map((item) => {
const Icon = iconMap[item.icon];
return (
<Link
key={`${item.label}-${item.link}`}
cta={{ link: item.link, linkType: "URL" }}
className="flex h-9 w-9 items-center justify-center rounded-full bg-[`#dcb65f`] text-white no-underline"
>
<Icon aria-hidden="true" />
<span className="sr-only">{item.label}</span>
</Link>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@starter/src/registry/hs1-albany/components/Hs1AlbanyFooterSection.tsx` around
lines 106 - 115, In Hs1AlbanyFooterSection, social link buttons rendered from
props.socialLinks currently only output SVG icons (Icon) inside Link, making
them inaccessible; update the Link to include an accessible name by adding an
aria-label={item.label} (or include visually-hidden text with item.label) on the
Link component so screen readers receive the label for each Icon, keeping the
existing key and cta props intact.

Comment on lines +138 to +144
const resolvedHeroImage = resolveComponentData(
props.heroImage,
locale,
streamDocument,
);
void resolvedHeroImage;
const heroImageUrl = capturedHeroImageUrl;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Resolved hero image is discarded — entity-selected images won't render.

The resolvedHeroImage is computed from props.heroImage but immediately voided. The component then uses a hardcoded static asset (capturedHeroImageUrl) instead. This prevents users from seeing their entity-selected or configured hero images.

Based on context snippet 1, resolveComponentData returns a TranslatableAssetImage containing the url property that should be used.

🔧 Proposed fix to use the resolved image
   const resolvedHeroImage = resolveComponentData(
     props.heroImage,
     locale,
     streamDocument,
   );
-  void resolvedHeroImage;
-  const heroImageUrl = capturedHeroImageUrl;
+  const heroImageUrl =
+    (resolvedHeroImage as { url?: string } | undefined)?.url ??
+    capturedHeroImageUrl;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const resolvedHeroImage = resolveComponentData(
props.heroImage,
locale,
streamDocument,
);
void resolvedHeroImage;
const heroImageUrl = capturedHeroImageUrl;
const resolvedHeroImage = resolveComponentData(
props.heroImage,
locale,
streamDocument,
);
const heroImageUrl =
(resolvedHeroImage as { url?: string } | undefined)?.url ??
capturedHeroImageUrl;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@starter/src/registry/hs1-albany/components/Hs1AlbanyHeroSection.tsx` around
lines 138 - 144, The code computes resolvedHeroImage via
resolveComponentData(props.heroImage, locale, streamDocument) but immediately
discards it and always uses capturedHeroImageUrl; replace that by using the
resolved asset's url (e.g., heroImageUrl = (resolvedHeroImage as
TranslatableAssetImage)?.url ?? capturedHeroImageUrl) and remove the void
resolvedHeroImage; ensure you reference resolvedHeroImage and fall back to
capturedHeroImageUrl so entity-selected images render correctly.

Comment on lines +255 to +263
{item.hours.includes(" - ") ? (
<>
{item.hours.split(" - ")[0]}
<span> - </span>
{item.hours.split(" - ")[1]}
</>
) : (
item.hours
)}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Edge case: Multiple intervals display incorrectly in desktop view.

When formatHoursForDay returns multiple intervals joined with ", " (e.g., "9:00 am - 12:00 pm, 1:00 pm - 5:00 pm"), splitting on " - " produces incorrect segments. The current logic would display "9:00 am" and "12:00 pm, 1:00 pm" instead of the full hours string.

The mobile view (line 286) correctly displays item.hours without splitting.

🔧 Proposed fix to handle multiple intervals
                 <p
                   className="mb-0 mt-3 text-[11px] text-[`#4f4e4e`]"
                   style={{ fontFamily: "Montserrat, Open Sans, sans-serif" }}
                 >
-                  {item.hours.includes(" - ") ? (
-                    <>
-                      {item.hours.split(" - ")[0]}
-                      <span> - </span>
-                      {item.hours.split(" - ")[1]}
-                    </>
-                  ) : (
-                    item.hours
-                  )}
+                  {item.hours}
                 </p>

Alternatively, if line breaking between start/end is needed, split only the first occurrence:

-                  {item.hours.includes(" - ") ? (
+                  {item.hours.includes(" - ") && !item.hours.includes(", ") ? (
                     <>
                       {item.hours.split(" - ")[0]}
                       <span> - </span>
                       {item.hours.split(" - ")[1]}
                     </>
                   ) : (
                     item.hours
                   )}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{item.hours.includes(" - ") ? (
<>
{item.hours.split(" - ")[0]}
<span> - </span>
{item.hours.split(" - ")[1]}
</>
) : (
item.hours
)}
{item.hours}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@starter/src/registry/hs1-albany/components/Hs1AlbanyHoursSection.tsx` around
lines 255 - 263, The desktop rendering in Hs1AlbanyHoursSection.tsx currently
splits item.hours on every " - ", which breaks multi-interval strings from
formatHoursForDay; update the conditional so if item.hours contains a comma (",
") you render item.hours as-is, otherwise split only the first " - " occurrence
(e.g., find the firstIndexOf " - " and use substring before/after) when
constructing the two parts; reference the item.hours usage in the JSX and adjust
the split logic to use indexOf/substring (or a single split with a limit)
instead of splitting on all " - "s.

Comment on lines +133 to +139
<div className="mb-1 flex items-end gap-1">
<span className="text-[26px] leading-none text-[#7b7b7b]">
--
</span>
<span className="text-[15px] font-bold uppercase tracking-[0.08em]">
mi
</span>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

The distance badge is shipping a placeholder.

Nothing in this component ever replaces --, so the template will always render -- mi. Wire it to real data or remove the badge until that value exists.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@starter/src/registry/hs1-albany/components/Hs1AlbanyLocationSection.tsx`
around lines 133 - 139, The distance badge in Hs1AlbanyLocationSection currently
renders a hardcoded "--", so either wire it to the real distance value or hide
the badge when no distance exists: locate the Hs1AlbanyLocationSection component
and replace the static "--" text with the actual distance variable (e.g.,
props.distance or computed formattedDistance) and ensure you format it
(round/trim) and append "mi"; alternatively, wrap the entire <div
className="mb-1 flex items-end gap-1"> block in a conditional render (e.g., only
render when distance != null/undefined) so the placeholder never appears if the
data is absent.

Comment on lines +91 to +106
<form className="grid gap-4 md:grid-cols-[1fr_1fr_auto]">
<input
className="h-[44px] border border-white/80 bg-transparent px-3 text-[13px] text-white placeholder:text-white/80"
placeholder="Enter your name (Required)"
/>
<input
className="h-[44px] border border-white/80 bg-transparent px-3 text-[13px] text-white placeholder:text-white/80"
placeholder="Enter email (Required)"
/>
<button
type="button"
className="h-[44px] border border-white/80 px-8 text-[13px] font-bold uppercase tracking-[0.08em] text-white"
style={{ fontFamily: "Nunito Sans, Open Sans, sans-serif" }}
>
Submit
</button>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

The signup form can't be submitted.

This repeats the same type="button"/no submit handler pattern, so the CTA never does anything. Because the inputs rely only on placeholders, the published form would also miss proper labels. If this is demo chrome only, render it as a mock instead of a real form.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@starter/src/registry/hs1-albany/components/Hs1AlbanySignupFormSection.tsx`
around lines 91 - 106, The form in Hs1AlbanySignupFormSection uses a plain
<form> with inputs that only have placeholders and a button set to
type="button", so it never submits and lacks accessible labels; change the
button in this component to type="submit" and add an onSubmit handler (e.g.,
handleSignupSubmit) on the <form> to process/validate the name/email, or if this
is purely demo chrome, replace the <form> with a non-interactive container (or
explicitly mark it as mock) and keep the CTA as a non-submitting element;
additionally, add proper <label> elements or visible/aria-label attributes for
the two inputs (name/email) and wire them to state or form refs so the submit
handler can read values.

Comment on lines +104 to +133
const activeQuote = props.quotes[0];
const quoteText =
activeQuote &&
resolveComponentData(activeQuote.quote.text, locale, streamDocument);

return (
<section className="bg-[#4f4e4e] px-6 py-12">
<div className="mx-auto max-w-[1170px]">
<h2
className="mb-8 mt-0 text-center text-white"
style={{
fontFamily: "Montserrat, Open Sans, sans-serif",
fontSize: `${props.heading.fontSize}px`,
fontWeight: props.heading.fontWeight,
letterSpacing: "1px",
textTransform:
props.heading.textTransform === "normal"
? undefined
: props.heading.textTransform,
}}
>
{heading}
</h2>
<div className="mx-auto max-w-[840px] bg-white px-8 py-10 text-center">
<div className="mx-auto mb-6 flex h-11 w-11 items-center justify-center rounded-full bg-[#d3a335] text-2xl text-white">
</div>
<p className="m-0 text-[22px] leading-8 text-[#4a4a4a]">
{quoteText}
</p>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Only the first testimonial is ever rendered.

quotes is modeled as an editable array of styled items, but this component always reads props.quotes[0] and prints it inside a hard-coded <p>. Every additional testimonial and all per-quote typography controls are ignored. Either render the array or collapse this to a single quote field so the editor matches runtime behavior.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@starter/src/registry/hs1-albany/components/Hs1AlbanyTestimonialsSection.tsx`
around lines 104 - 133, The component currently only uses props.quotes[0]
(activeQuote) and always renders a single <p>, so additional testimonials and
per-quote typography are ignored; update Hs1AlbanyTestimonialsSection to iterate
over props.quotes (e.g., map) and for each quote call
resolveComponentData(quote.quote.text, locale, streamDocument) to compute
quoteText, render each testimonial block (including the quote mark, text, and
any per-quote typography props from the quote item) instead of the hard-coded
single <p>, and remove or repurpose the activeQuote variable if you keep a
single-view mode; ensure you reference props.quotes, resolveComponentData, and
any per-quote typography fields when implementing the mapping.

Comment on lines +146 to +160
<p
className="mb-0 mt-5 text-[22px] text-[#d3a335]"
style={{
fontFamily: "Montserrat, Open Sans, sans-serif",
lineHeight: "1.4",
}}
>
{subtitle}
</p>
<p
className="mb-0 mt-5 text-[14px] leading-7 text-[#7a7a7a]"
style={{ fontFamily: "Montserrat, Open Sans, sans-serif" }}
>
{body}
</p>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Subtitle and body typography controls aren't wired up.

Both props are declared as StyledTextProps, but this render path never applies their fontSize, fontColor, fontWeight, or textTransform values. Edits to those controls in Puck won't show up until the styles come from props instead of fixed classes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@starter/src/registry/hs1-albany/components/Hs1AlbanyWelcomeSection.tsx`
around lines 146 - 160, The subtitle and body render blocks in
Hs1AlbanyWelcomeSection are using fixed classes/styles so the passed-in
StyledTextProps (subtitle and body) fontSize, fontColor, fontWeight, and
textTransform are ignored; update the subtitle and body render logic to consume
those props: compute style entries (fontSize -> fontSize, fontColor -> color,
fontWeight -> fontWeight, textTransform -> textTransform, plus existing
fontFamily/lineHeight where applicable) and merge them into the inline style
object, and/or compute className fragments from those prop values instead of
hardcoded text sizes/colors; target the JSX elements that reference subtitle and
body in Hs1AlbanyWelcomeSection.tsx and replace the fixed className/style usage
so the StyledTextProps values are applied at render time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant