Skip to content

feat: get locator working in local dev fake starter#1156

Merged
mkilpatrick merged 6 commits intomainfrom
locator-starter
Apr 14, 2026
Merged

feat: get locator working in local dev fake starter#1156
mkilpatrick merged 6 commits intomainfrom
locator-starter

Conversation

@mkilpatrick
Copy link
Copy Markdown
Collaborator

No description provided.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 14, 2026

Walkthrough

This PR centralizes local-development detection by adding isFakeStarterLocalDev() and replacing scattered hardcoded localhost checks. It adds utilities isFakeStarterLocalDevRoute/isFakeStarterLocalDev, exports getOriginsForSending, and introduces getLocalDevLayoutData. Template metadata generation (generateTemplateMetadata) now accepts an optional stream document and includes locator-aware fields. A new dev-locator VE template, local-dev locator mocks, tests for the new utilities and metadata, and starter mapping/config updates were also added. Several editor components now gate local-dev branches using the new helper.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant User
participant Editor
participant ParentFrame
Note over Editor,ParentFrame: Flow when selecting assets or editing rich/code fields
User->>Editor: Trigger "select asset" / "edit field"
alt local-dev (isFakeStarterLocalDev() == true)
Editor->>User: prompt() for input (URL/HTML/code)
User-->>Editor: returns value
Editor->>Editor: apply via handleNewValue/onChange
else normal mode
Editor->>ParentFrame: postMessage(open selector / open drawer)
ParentFrame-->>Editor: postMessage(selection closed with payload)
Editor->>Editor: verify messageId -> apply via handleNewValue/onChange
end

Possibly related PRs

Suggested labels

create-dev-release

Suggested reviewers

  • jwartofsky-yext
  • asanehisa
  • briantstephan
🚥 Pre-merge checks | ✅ 1 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive No pull request description was provided by the author, making it impossible to assess relevance or coherence. Add a description explaining the changes, scope, and rationale for enabling Locator template support in local dev.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main objective of the PR—enabling Locator template support in the local development fake starter environment.

✏️ 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 locator-starter

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: 2

🧹 Nitpick comments (3)
starter/src/ve.config.tsx (1)

36-38: Consider using a more specific type for locatorComponentRegistry.

The mainComponentRegistry uses Config<DevProps> for type safety, but locatorComponentRegistry uses Config<any>. Since locatorConfig is typed as Config<LocatorConfigProps>, you could import and use that type for consistency.

💡 Suggested type improvement
 import {
   DirectoryCategory,
   DirectoryCategoryComponents,
   DirectoryCategoryProps,
   MainConfigProps,
   locatorConfig,
   mainConfig,
+  LocatorConfigProps,
 } from "@yext/visual-editor";

 // ...

-export const locatorComponentRegistry: Record<string, Config<any>> = {
+export const locatorComponentRegistry: Record<string, Config<LocatorConfigProps>> = {
   dev: locatorConfig,
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@starter/src/ve.config.tsx` around lines 36 - 38, locatorComponentRegistry is
typed as Record<string, Config<any>) which loses type safety; replace the any
with the specific LocatorConfigProps type used by locatorConfig. Import or
reference LocatorConfigProps and change the registry type to Record<string,
Config<LocatorConfigProps>> (or the same pattern used by mainComponentRegistry)
so locatorComponentRegistry and locatorConfig share the precise Config generic
and provide compile-time validation.
packages/visual-editor/src/internal/hooks/useMessage.test.ts (1)

88-95: Good test coverage for the new behavior, consider adding a non-fake-starter case.

The test correctly validates that getOriginsForSending returns only window.location.origin when on a fake starter local dev route. For completeness, consider adding a test case verifying the default behavior when not on a fake starter route (i.e., that the original targetOrigins array is returned/used).

💡 Suggested additional test case
 describe("getOriginsForSending", () => {
   it("uses the current origin only on fake starter local dev routes", () => {
     window.history.replaceState({}, "", "/dev-locator/example");
 
     expect(getOriginsForSending(["https://dev.yext.com"])).toEqual([
       window.location.origin,
     ]);
   });
+
+  it("includes target origins on non-fake-starter routes", () => {
+    window.history.replaceState({}, "", "/edit");
+
+    const result = getOriginsForSending(["https://dev.yext.com"]);
+    expect(result).toContain("https://dev.yext.com");
+  });
 });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/visual-editor/src/internal/hooks/useMessage.test.ts` around lines 88
- 95, Add a complementary unit test for getOriginsForSending that verifies
non-fake-starter behavior: set window.history to a non-fake-starter path (not
matching "/dev-locator/*"), call getOriginsForSending with a sample
targetOrigins array (e.g., ["https://dev.yext.com"]), and assert that it returns
the original targetOrigins array unchanged; reference getOriginsForSending to
locate where to add the test and mirror the existing test structure for setup
and teardown of window.history.
starter/localData/dev-locator-stream__en__8967748.json (1)

7-7: The serialized layout JSON appears valid, but consider extracting to a separate file for maintainability.

The embedded JSON string in __.layout is difficult to read and maintain inline. For local development mock data, consider storing the layout configuration in a separate JSON file and referencing it, or at minimum adding a comment explaining the structure.

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

In `@starter/localData/dev-locator-stream__en__8967748.json` at line 7, The inline
serialized JSON assigned to "layout" is hard to maintain; extract the large
layout string into a separate JSON file (e.g., a dev fixture) and replace the
inline value with a lightweight reference or loader. Locate the "layout" key in
this file and move the object containing root props (version/title/description),
content/MainContent (id MainContent-b6c58f1c-102d-46af-a58c-65751efa30da) and
the Locator config (id Locator-2ae506f4-a3ee-46ea-b5f9-e4c3236243a7, mapStyle,
filters, resultCard, distanceDisplay) into that new file; update any readers to
parse/load the external JSON and add a concise comment explaining the layout
format for future devs.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/visual-editor/src/internal/types/templateMetadata.ts`:
- Around line 79-94: The entries for emails and services in templateMetadata are
declared as scalar strings but actual fixtures return arrays; update the emails
and services objects (their field_id "emails" and "services") so their
field_type and field_type_id reflect a list type (e.g., switch field_type_id
from "type.string" to the project’s list-string identifier and adjust
field_type.variant_type to the list variant) or remove these entries from the
template metadata until list-valued locator handling is implemented; modify only
the emails and services objects in templateMetadata.ts (field_type,
field_type.variant_type, and field_type_id) to align with the fixtures.

In `@starter/src/templates/dev-locator.tsx`:
- Around line 119-128: The mock installation is happening during render
(installLocatorLocalDevMocks called at top-level), which can patch globals
without a paired cleanup if render is aborted; move the call into the existing
React.useEffect so the effect both installs and cleans up the mocks.
Specifically, remove the top-level installLocatorLocalDevMocks invocation that
sets mockCleanupRef.current, and instead inside the React.useEffect call
installLocatorLocalDevMocks(), assign its returned cleanup to
mockCleanupRef.current, and keep the existing return cleanup that calls
mockCleanupRef.current?.() and nulls it; ensure this prevents mutating
globalThis.fetch or navigator.geolocation.getCurrentPosition during render.

---

Nitpick comments:
In `@packages/visual-editor/src/internal/hooks/useMessage.test.ts`:
- Around line 88-95: Add a complementary unit test for getOriginsForSending that
verifies non-fake-starter behavior: set window.history to a non-fake-starter
path (not matching "/dev-locator/*"), call getOriginsForSending with a sample
targetOrigins array (e.g., ["https://dev.yext.com"]), and assert that it returns
the original targetOrigins array unchanged; reference getOriginsForSending to
locate where to add the test and mirror the existing test structure for setup
and teardown of window.history.

In `@starter/localData/dev-locator-stream__en__8967748.json`:
- Line 7: The inline serialized JSON assigned to "layout" is hard to maintain;
extract the large layout string into a separate JSON file (e.g., a dev fixture)
and replace the inline value with a lightweight reference or loader. Locate the
"layout" key in this file and move the object containing root props
(version/title/description), content/MainContent (id
MainContent-b6c58f1c-102d-46af-a58c-65751efa30da) and the Locator config (id
Locator-2ae506f4-a3ee-46ea-b5f9-e4c3236243a7, mapStyle, filters, resultCard,
distanceDisplay) into that new file; update any readers to parse/load the
external JSON and add a concise comment explaining the layout format for future
devs.

In `@starter/src/ve.config.tsx`:
- Around line 36-38: locatorComponentRegistry is typed as Record<string,
Config<any>) which loses type safety; replace the any with the specific
LocatorConfigProps type used by locatorConfig. Import or reference
LocatorConfigProps and change the registry type to Record<string,
Config<LocatorConfigProps>> (or the same pattern used by mainComponentRegistry)
so locatorComponentRegistry and locatorConfig share the precise Config generic
and provide compile-time validation.
🪄 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: 0d4e2b80-5565-45bf-9120-d1d00a1a1dbd

📥 Commits

Reviewing files that changed from the base of the PR and between 879a7db and a47b0c6.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (21)
  • packages/visual-editor/src/editor/TranslatableRichTextField.tsx
  • packages/visual-editor/src/fields/CodeField.tsx
  • packages/visual-editor/src/internal/components/AdvancedSettings.tsx
  • packages/visual-editor/src/internal/hooks/useMessage.test.ts
  • packages/visual-editor/src/internal/hooks/useMessage.ts
  • packages/visual-editor/src/internal/hooks/useMessageReceivers.test.ts
  • packages/visual-editor/src/internal/hooks/useMessageReceivers.ts
  • packages/visual-editor/src/internal/puck/constant-value-fields/Image.tsx
  • packages/visual-editor/src/internal/puck/constant-value-fields/Video.tsx
  • packages/visual-editor/src/internal/types/templateMetadata.test.ts
  • packages/visual-editor/src/internal/types/templateMetadata.ts
  • packages/visual-editor/src/utils/isFakeStarterLocalDev.test.ts
  • packages/visual-editor/src/utils/isFakeStarterLocalDev.ts
  • starter/localData/dev-locator-stream__en__8967748.json
  • starter/localData/mapping.json
  • starter/package.json
  • starter/src/mocks/locatorLocalDev.ts
  • starter/src/templates/dev-locator.tsx
  • starter/src/templates/dev.tsx
  • starter/src/ve.config.tsx
  • starter/tailwind.config.ts

Comment thread packages/visual-editor/src/internal/types/templateMetadata.ts
Comment thread starter/src/templates/dev-locator.tsx Outdated
Comment thread packages/visual-editor/src/internal/hooks/useMessage.ts
Comment thread starter/src/mocks/locatorLocalDev.ts
asanehisa
asanehisa previously approved these changes Apr 14, 2026
Copy link
Copy Markdown
Contributor

@asanehisa asanehisa left a comment

Choose a reason for hiding this comment

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

will conflict with work brian / i did in other branch but thats ok haha

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: 2

🤖 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/mocks/locatorLocalDev.ts`:
- Around line 216-218: The current mock captures only the function value in
originalGetCurrentPosition and later restores via Object.defineProperty with
only a value, leaving an own-property shadow if the original was inherited;
instead capture the original property descriptor (use
Object.getOwnPropertyDescriptor on navigator or Geolocation.prototype as needed)
before defining the mock, and on teardown restore that full descriptor via
Object.defineProperty (or, if no descriptor existed originally, delete the
mocked own property). Update references around geolocation,
originalGetCurrentPosition, and the Object.defineProperty mock/teardown logic to
use the saved descriptor and conditional delete/restore behavior.

In `@starter/src/templates/dev-locator.tsx`:
- Around line 71-89: The current snippet replaces window.WebSocket with a bare
function (assigned in the IIFE using originalWebSocket and window.WebSocket)
which loses WebSocket.prototype and static properties (e.g., WebSocket.OPEN) and
breaks instanceof checks; update the replacement to preserve the native
constructor shape by creating a wrapper that either extends the original
constructor or returns a Proxy around originalWebSocket so prototype chain and
static constants remain intact, while intercepting the constructor call to
detect and short-circuit HMR URLs (the logic checking
url.includes('24678'|'vite'|'hmr') and the blocked-return object should remain
but must be returned only for those URLs), ensure allowed connections call
through to originalWebSocket with same signature and preserve
originalWebSocket.prototype and static members.
🪄 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: f8890515-4e00-4677-b1fe-b9010db7ac03

📥 Commits

Reviewing files that changed from the base of the PR and between a47b0c6 and 0e03b8c.

⛔ Files ignored due to path filters (1)
  • packages/visual-editor/src/components/testing/screenshots/Locator/[tablet] latest version default props.png is excluded by !**/*.png, !packages/visual-editor/src/components/testing/screenshots/**
📒 Files selected for processing (2)
  • starter/src/mocks/locatorLocalDev.ts
  • starter/src/templates/dev-locator.tsx

Comment thread starter/src/mocks/locatorLocalDev.ts Outdated
Comment thread starter/src/templates/dev-locator.tsx Outdated
Comment thread packages/visual-editor/src/internal/hooks/useMessageReceivers.ts Outdated
Comment thread starter/localData/dev-locator-stream__en__8967748.json Outdated
Comment thread starter/src/ve.config.tsx Outdated
Copy link
Copy Markdown
Contributor

@jwartofsky-yext jwartofsky-yext left a comment

Choose a reason for hiding this comment

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

lgtm!

@mkilpatrick mkilpatrick dismissed stale reviews from jwartofsky-yext and asanehisa via fe8273b April 14, 2026 20:30
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: 1

🧹 Nitpick comments (1)
starter/src/templates/dev-locator.tsx (1)

113-120: Expose the mode toggle as a pressed button.

This control behaves like a toggle, so adding aria-pressed will make the current state much clearer to assistive tech.

Suggested fix
         <button
+          type="button"
+          aria-pressed={themeMode}
           className={"toggle-button"}
           onClick={() => {
             setThemeMode(!themeMode);
           }}
         >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@starter/src/templates/dev-locator.tsx` around lines 113 - 120, The toggle
button currently rendered by the JSX using themeMode and setThemeMode should
expose its state to assistive tech by adding an aria-pressed attribute bound to
the boolean themeMode; update the button (className "toggle-button") to include
aria-pressed={themeMode} so screen readers know whether it is pressed, and keep
the existing onClick handler (setThemeMode(!themeMode)) and label logic intact.
🤖 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/mocks/locatorLocalDev.ts`:
- Around line 193-202: Clamp and validate the parsed pagination values before
returning them: after parsing requestBody into parsedBody, coerce
parsedBody.offset and parsedBody.limit to numbers and enforce offset >= 0
(fallback to 0 on invalid/negative) and limit between 1 and DEFAULT_LIMIT
(fallback to DEFAULT_LIMIT on invalid/<=0/too-large); return those sanitized
offset and limit values instead of raw parsedBody values so malformed or
negative inputs cannot produce surprising slice behavior.

---

Nitpick comments:
In `@starter/src/templates/dev-locator.tsx`:
- Around line 113-120: The toggle button currently rendered by the JSX using
themeMode and setThemeMode should expose its state to assistive tech by adding
an aria-pressed attribute bound to the boolean themeMode; update the button
(className "toggle-button") to include aria-pressed={themeMode} so screen
readers know whether it is pressed, and keep the existing onClick handler
(setThemeMode(!themeMode)) and label logic intact.
🪄 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: e744b6c9-5721-4988-8f7d-7784bb209702

📥 Commits

Reviewing files that changed from the base of the PR and between 0e03b8c and fe8273b.

📒 Files selected for processing (8)
  • packages/visual-editor/src/internal/hooks/useMessageReceivers.ts
  • packages/visual-editor/src/internal/types/templateMetadata.ts
  • starter/localData/dev-locator-stream__en__8967748.json
  • starter/src/mocks/locatorLocalDev.ts
  • starter/src/templates/dev-locator.tsx
  • starter/src/templates/dev.tsx
  • starter/src/utils.ts
  • starter/src/ve.config.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
  • starter/src/templates/dev.tsx
  • packages/visual-editor/src/internal/hooks/useMessageReceivers.ts
  • packages/visual-editor/src/internal/types/templateMetadata.ts
  • starter/localData/dev-locator-stream__en__8967748.json

Comment on lines +193 to +202
try {
const parsedBody = JSON.parse(requestBody) as {
offset?: number;
limit?: number;
};

return {
offset: parsedBody.offset ?? 0,
limit: parsedBody.limit ?? DEFAULT_LIMIT,
};
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

Clamp mocked pagination values before returning them.

Malformed numeric values still flow through here. For example, negative or zero offset/limit values will produce surprising slice() behavior instead of the stable default-page fallback this mock is aiming for.

Suggested fix
   try {
     const parsedBody = JSON.parse(requestBody) as {
       offset?: number;
       limit?: number;
     };
+    const offset =
+      Number.isInteger(parsedBody.offset) && parsedBody.offset >= 0
+        ? parsedBody.offset
+        : 0;
+    const limit =
+      Number.isInteger(parsedBody.limit) && parsedBody.limit > 0
+        ? parsedBody.limit
+        : DEFAULT_LIMIT;
 
     return {
-      offset: parsedBody.offset ?? 0,
-      limit: parsedBody.limit ?? DEFAULT_LIMIT,
+      offset,
+      limit,
     };
   } catch {
📝 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
try {
const parsedBody = JSON.parse(requestBody) as {
offset?: number;
limit?: number;
};
return {
offset: parsedBody.offset ?? 0,
limit: parsedBody.limit ?? DEFAULT_LIMIT,
};
try {
const parsedBody = JSON.parse(requestBody) as {
offset?: number;
limit?: number;
};
const offset =
Number.isInteger(parsedBody.offset) && parsedBody.offset >= 0
? parsedBody.offset
: 0;
const limit =
Number.isInteger(parsedBody.limit) && parsedBody.limit > 0
? parsedBody.limit
: DEFAULT_LIMIT;
return {
offset,
limit,
};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@starter/src/mocks/locatorLocalDev.ts` around lines 193 - 202, Clamp and
validate the parsed pagination values before returning them: after parsing
requestBody into parsedBody, coerce parsedBody.offset and parsedBody.limit to
numbers and enforce offset >= 0 (fallback to 0 on invalid/negative) and limit
between 1 and DEFAULT_LIMIT (fallback to DEFAULT_LIMIT on
invalid/<=0/too-large); return those sanitized offset and limit values instead
of raw parsedBody values so malformed or negative inputs cannot produce
surprising slice behavior.

@mkilpatrick mkilpatrick merged commit 19ffc41 into main Apr 14, 2026
16 checks passed
@mkilpatrick mkilpatrick deleted the locator-starter branch April 14, 2026 20:43
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.

4 participants