Skip to content

Conversation

@shige
Copy link
Member

@shige shige commented Oct 9, 2025

User description

Prevents unintended form submissions by explicitly setting type="button" when used as a native button element. The type prop can still be overridden when needed.

Summary

fix: default ClickableText button type to "button"

Related Issue

None.

Changes

Prevents unintended form submissions by explicitly setting type="button" when used as a native button element. The type prop can still be overridden when needed.

Testing

Other Information


PR Type

Bug fix


Description

  • Default button type to "button" to prevent form submissions

  • Maintain type prop override capability for flexibility

  • Refactor component logic for better prop handling


Diagram Walkthrough

flowchart LR
  A["ClickableText Component"] --> B["Check asChild prop"]
  B --> C["asChild = true: Use Slot with conditional type"]
  B --> D["asChild = false: Use button with type='button' default"]
  C --> E["Preserve type override capability"]
  D --> E
Loading

File Walkthrough

Relevant files
Bug fix
clickable-text.tsx
Default button type to prevent form submissions                   

apps/studio.giselles.ai/components/ui/clickable-text.tsx

  • Extract type prop from component props for explicit handling
  • Add conditional logic to set default type="button" for native button
    elements
  • Preserve type override capability when asChild is true
  • Refactor component structure with shared props object
+17/-8   

Summary by CodeRabbit

  • New Features
    • Clickable text now accepts and respects the button type, enabling finer control (e.g., submit, reset, button) across usages, including when used as a child component.
  • Bug Fixes
    • Defaults clickable text to a non-submitting button to prevent unintended form submissions.
    • Ensures consistent behavior and prop forwarding whether used standalone or as a child, improving reliability in complex layouts.

Prevents unintended form submissions by explicitly setting type="button" when used as a native button element. The type prop can still be overridden when needed.
@shige shige self-assigned this Oct 9, 2025
@Copilot Copilot AI review requested due to automatic review settings October 9, 2025 05:33
@changeset-bot
Copy link

changeset-bot bot commented Oct 9, 2025

⚠️ No Changeset found

Latest commit: 6c1f55e

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

💥 An error occurred when fetching the changed packages and changesets in this PR
Some errors occurred when validating the changesets config:
The package or glob expression "giselle-sdk" is specified in the `ignore` option but it is not found in the project. You may have misspelled the package name or provided an invalid glob expression. Note that glob expressions must be defined according to https://www.npmjs.com/package/micromatch.

@giselles-ai
Copy link

giselles-ai bot commented Oct 9, 2025

Finished running flow.

Step Status Updated(UTC)
1 Oct 9, 2025 5:33am
2 Oct 9, 2025 5:34am
3 Oct 9, 2025 5:34am
4 Oct 9, 2025 5:34am

@vercel
Copy link

vercel bot commented Oct 9, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
giselle Ready Ready Preview Comment Oct 9, 2025 5:38am

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

Prevents unintended form submissions by setting a default type="button" for the ClickableText component when used as a native button element, while still allowing the type prop to be overridden when needed.

  • Added type prop to the component interface and destructuring
  • Restructured the component to handle type prop correctly for both asChild and native button usage
  • Set default type="button" for native button elements while preserving override capability

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

<Comp
{...sharedProps}
{...props}
{...(type !== undefined && { type })}
Copy link

Copilot AI Oct 9, 2025

Choose a reason for hiding this comment

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

The conditional spread operator with undefined check is unnecessarily complex. Since Slot components pass through all props, you can simply spread type directly: {...props} already includes the type prop, making the conditional spread redundant.

Suggested change
{...(type !== undefined && { type })}

Copilot uses AI. Check for mistakes.
@qodo-merge-for-open-source
Copy link

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
🟢
No security concerns identified No security vulnerabilities detected by AI analysis. Human verification advised for critical code.
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
No custom compliance provided

Follow the guide to enable custom compliance check.

Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

<Comp
{...sharedProps}
{...props}
{...(type !== undefined && { type })}
Copy link

Choose a reason for hiding this comment

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

Bug: Inconsistent Prop Behavior Causes Form Submission Issues

When asChild is true, the type prop is only passed if explicitly defined, unlike the non-asChild case which defaults to type="button". This inconsistency means button elements used with asChild will default to type="submit", potentially causing unintended form submissions.

Fix in Cursor Fix in Web

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 9, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Implements explicit handling for the button type prop in ClickableText, introducing a branch-based render for asChild vs non-asChild. Builds sharedProps (className, ref), conditionally spreads type, and defaults type to "button" when not provided in the non-asChild path. Ensures ClickableText is exported.

Changes

Cohort / File(s) Summary
UI Component: ClickableText
apps/studio.giselles.ai/components/ui/clickable-text.tsx
Refactors render into asChild vs non-asChild branches; adds sharedProps (className, ref); conditionally forwards type when asChild; defaults type to "button" when not asChild; explicitly exports ClickableText.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Caller
  participant ClickableText
  participant Child as Child Element
  participant Button as Button Element

  Caller->>ClickableText: props { asChild?, type?, className, ref, ... }
  alt asChild = true
    Note over ClickableText: Build sharedProps { className, ref }<br/>Include type only if provided
    ClickableText->>Child: Render with {...sharedProps, ...props, type?}
  else asChild = false
    Note over ClickableText: Build sharedProps { className, ref }<br/>type = props.type ?? "button"
    ClickableText->>Button: Render with {...sharedProps, ...props, type}
  end
Loading

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

A bunny taps a tidy key,
Clickable words now button-y.
If it’s a child, we pass along—
If not, “button” sings its song.
Props align, no fuss, no fight—
Hop, hop, ship it—type’s just right. 🐇✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The title succinctly and accurately summarizes the primary change by indicating the default ClickableText button type is set to “button,” clearly reflecting the fix implemented in the changeset.
Description Check ✅ Passed The description correctly uses the repository template with Summary, Related Issue, and Changes sections populated, but the Testing and Other Information sections remain as unfilled placeholders.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/clickable-text-button-type

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 12c8c19 and 6c1f55e.

📒 Files selected for processing (1)
  • apps/studio.giselles.ai/components/ui/clickable-text.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/development-guide.mdc)

**/*.{ts,tsx}: Use Biome for formatting with tab indentation and double quotes
Follow organized imports pattern (enabled in biome.json)
Use TypeScript for type safety; avoid any types
Use Next.js patterns for web applications
Use async/await for asynchronous code rather than promises
Error handling: use try/catch blocks and propagate errors appropriately
Use kebab-case for all filenames (e.g., user-profile.ts)
Use camelCase for variables, functions, and methods (e.g., userEmail)
Use prefixes like is, has, can, should for boolean variables and functions for clarity
Use verbs or verb phrases that clearly indicate purpose for function naming (e.g., calculateTotalPrice(), not process())

If breaking changes are introduced in new AI SDK versions, update code to accommodate those changes

**/*.{ts,tsx}: Avoid using the any type in TypeScript
Use async/await for asynchronous code and include proper error handling
Variables and functions should be camelCase
Boolean variables and functions should use is/has/can/should prefixes where appropriate
Function names should clearly indicate their purpose

Files:

  • apps/studio.giselles.ai/components/ui/clickable-text.tsx
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/development-guide.mdc)

**/*.tsx: Use functional components with React hooks
Use PascalCase for React components and classes (e.g., UserProfile)

**/*.tsx: React components should use React hooks and Next.js patterns
Component identifiers (names) should be PascalCase

Files:

  • apps/studio.giselles.ai/components/ui/clickable-text.tsx
**/*

📄 CodeRabbit inference engine (.cursor/rules/naming-guide.mdc)

All filenames should use kebab-case (lowercase with hyphens)

Files:

  • apps/studio.giselles.ai/components/ui/clickable-text.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/naming-guide.mdc)

**/*.{js,jsx,ts,tsx}: React components and classes should use PascalCase
Variables, functions, and methods should use camelCase
Use verbs or verb phrases for function names; names should clearly indicate what the function does; avoid ambiguous names that could lead to misuse
Use nouns or noun phrases for variable names; names should describe what the variable represents; avoid single-letter variables except in very short scopes
Use prefixes like 'is', 'has', 'can', 'should' for both variables and functions returning boolean values; make the true/false meaning clear

Files:

  • apps/studio.giselles.ai/components/ui/clickable-text.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: check
🔇 Additional comments (3)
apps/studio.giselles.ai/components/ui/clickable-text.tsx (3)

23-23: LGTM: Clean prop destructuring.

Destructuring type separately allows explicit handling in the render branches below.


25-28: LGTM: Clean extraction of shared props.

The sharedProps pattern eliminates duplication between the two render branches.


40-40: LGTM: Correct default type for non-asChild path.

The type={type ?? "button"} correctly defaults to "button" when type is undefined, preventing unintended form submissions. The asChild branch should use the same pattern for consistency.


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.

@giselles-ai
Copy link

giselles-ai bot commented Oct 9, 2025

📋 Manual QA Checklist

Based on the changes in this PR, here are the key areas to test manually:

  • ClickableText (Default Behavior): Inside an HTML form, clicking the ClickableText component should not trigger a form submission. Verify the "Form Submitted!" alert does not appear.
  • ClickableText (Type="button"): Explicitly setting type="button" on ClickableText within a form should prevent form submission. Verify the "Form Submitted!" alert does not appear.
  • ClickableText (Type="submit"): Explicitly setting type="submit" on ClickableText within a form should trigger form submission. Verify the "Form Submitted!" alert appears.
  • ClickableText (asChild): When ClickableText uses asChild to wrap a non-button element (e.g., div) inside a form, it should not trigger form submission. Verify the "Form Submitted!" alert does not appear.
  • ClickableText (asChild with Type="submit"): When ClickableText uses asChild to wrap an element that has type="submit" (e.g., a <button type="submit">), it should trigger form submission. Verify the "Form Submitted!" alert appears.
  • ClickableText (Default Type Attribute): When ClickableText is rendered as a button within a form, inspect the rendered HTML element. It should have the type="button" attribute by default.

✨ Prompt for AI Agents

Use the following prompts with Cursor or Claude Code to automate E2E testing:

📝 E2E Test Generation Prompt
You are an expert QA engineer tasked with generating E2E tests for the `ClickableText` React component. The PR addresses a bug where `ClickableText` inside a form would incorrectly default to `type="submit"`, causing unintended form submissions. The fix ensures it defaults to `type="button"`.

**Objective:** Generate Playwright E2E tests to verify the fix and ensure backward compatibility for explicit `type="submit"` usage.

**Key Areas to Test:**
1.  **Default Behavior:** `ClickableText` inside a form should NOT submit the form.
2.  **Explicit Submit:** `ClickableText` with `type="submit"` should submit the form.
3.  **`asChild` Prop (No Submit):** `ClickableText` with `asChild` wrapping a non-submit element should not submit.
4.  **`asChild` Prop (With Submit):** `ClickableText` with `asChild` wrapping a `type="submit"` element should submit.

**Test Setup:**
*   Create a test page using `page.setContent()` that includes a simple HTML `<form action="/success">`.
*   Use `data-testid` attributes for selectors (e.g., `data-testid="test-form"`, `data-testid="clickable-text-wrapper"`).

**Test Scenarios & Assertions:**

1.  **Scenario: Default ClickableText in Form**
    *   **Setup:** Render a form containing a `ClickableText` component without any `type` prop.
    *   **Action:** Click the `ClickableText` component.
    *   **Assertion:** Verify the page URL remains unchanged (no form submission). Use `await page.waitForTimeout()` and then check `page.url()`.

2.  **Scenario: ClickableText with type="submit" in Form**
    *   **Setup:** Render a form containing a `ClickableText` component with `type="submit"`.
    *   **Action:** Click the `ClickableText` component.
    *   **Assertion:** Verify the page URL changes to `/success` using `await expect(page).toHaveURL('/success');`.

3.  **Scenario: ClickableText with asChild and no type in Form**
    *   **Setup:** Render a form containing `<ClickableText asChild><div>Click Me</div></ClickableText>`. Simulate the rendered output of a `div` with `type="button"` implicitly added by the component when it's not a submit.
    *   **Action:** Click the `div` element.
    *   **Assertion:** Verify the page URL remains unchanged.

4.  **Scenario: ClickableText with asChild and type="submit" in Form**
    *   **Setup:** Render a form containing `<ClickableText asChild><button type="submit">Submit Button</button></ClickableText>`.
    *   **Action:** Click the `button` element.
    *   **Assertion:** Verify the page URL changes to `/success`.

**MCP Integration:**
*   Tests should be runnable via `npx playwright test`.
*   Ensure test files are correctly named (e.g., `clickable-text.spec.ts`) and located in a `tests/` directory.

**Output:** Generate a complete Playwright test file (`.spec.ts`) including necessary imports, test descriptions, setup, actions, and assertions for all scenarios. Include comments explaining the logic where necessary.

@qodo-merge-for-open-source
Copy link

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
General
Refactor component to remove duplication

Refactor the component to eliminate the if/else block by creating a single
componentProps object that conditionally includes the type property, thus
simplifying the return statement.

apps/studio.giselles.ai/components/ui/clickable-text.tsx [25-40]

-		const sharedProps = {
+		const componentProps = {
 			className: cn(clickableTextVariant({ variant, className })),
 			ref,
+			...props,
+			...(!asChild && { type: type ?? "button" }),
+			...(asChild && type !== undefined && { type }),
 		};
 
-		if (asChild) {
-			return (
-				<Comp
-					{...sharedProps}
-					{...props}
-					{...(type !== undefined && { type })}
-				/>
-			);
-		}
+		return <Comp {...componentProps} />;
 
-		return <Comp {...sharedProps} {...props} type={type ?? "button"} />;
-
  • Apply / Chat
Suggestion importance[1-10]: 6

__

Why: The suggestion correctly refactors the component to remove the if/else block, reducing code duplication and improving readability by consolidating prop handling into a single object.

Low
Learned
best practice
Do not forward button-only props

Avoid forwarding type when asChild is true to prevent leaking button-only props
to arbitrary children; rely on consumers to set appropriate props on their own
element.

apps/studio.giselles.ai/components/ui/clickable-text.tsx [30-38]

 if (asChild) {
   return (
     <Comp
       {...sharedProps}
       {...props}
-      {...(type !== undefined && { type })}
     />
   );
 }
  • Apply / Chat
Suggestion importance[1-10]: 6

__

Why:
Relevant best practice - Use safe defaults and guard clauses in UI components; avoid passing unsupported or redundant props when switching underlying element types.

Low
  • More

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant