Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughThis PR introduces three new form-related components—Field, Fieldset, and Form—to the raystack library. Field provides label, error, and helper text management as a wrapper component. Fieldset groups related fields semantically. Form wraps native HTML form elements. Concurrently, InputField and TextArea components are refactored to remove label/error responsibility, delegating these concerns to the new Field wrapper. Documentation, tests, demos, and playground examples are included. Changes
Sequence DiagramsequenceDiagram
actor User
participant Form
participant Fieldset
participant Field
participant InputField
participant Validation
User->>Form: Submit form
Form->>Validation: Validate fields
Validation->>Fieldset: Check fieldset state
Fieldset->>Field: Validate each field
Field->>InputField: Get input value
InputField-->>Field: Return value
Field->>Validation: Run field validator
Validation-->>Field: Return error/valid
Field-->>Fieldset: Field result
Fieldset-->>Form: Fieldset validation result
Validation-->>Form: All validations complete
Form-->>User: Display errors or submit
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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 (4)
packages/raystack/components/text-area/text-area.tsx (3)
24-24:⚠️ Potential issue | 🟡 Minor
style.widthis currently overridden by default width.Line 40 uses
{ ...style, width }, so with defaultwidth='100%'(Line 24), a consumer-providedstyle.widthis ignored.Suggested fix
- style={{ ...style, width }} + style={{ width, ...style }}Also applies to: 40-40
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/raystack/components/text-area/text-area.tsx` at line 24, The component sets a default width prop that ends up overriding any consumer-provided style.width because the JSX merges props using "{ ...style, width }"; update the merge so consumer styles win by placing the default width first and then spreading style (e.g., change the merge in the TextArea component where style and width are combined to use width before spreading style) or conditionally apply width only when style.width is not provided; locate this in the TextArea function/props handling to adjust the object merge order for style and width.
9-16:⚠️ Potential issue | 🟠 MajorBreaking prop-removal migration incomplete.
TextAreano longer acceptslabelin its props interface, but it's still used atapps/www/src/app/examples/page.tsx:1641. This will cause a type error or silently drop the label UI.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/raystack/components/text-area/text-area.tsx` around lines 9 - 16, TextAreaProps no longer declares a label prop but callers (e.g., the example page that passes label to TextArea) still rely on it; either restore the label prop on the component or remove those usages. To fix, add an optional label?: React.ReactNode (or string) to the TextAreaProps interface and update the TextArea component to accept and render that label (e.g., render label above the textarea when label is provided), or alternatively remove the label prop usage where TextArea is instantiated. Update references to TextAreaProps and the TextArea component accordingly so the types and runtime UI stay in sync.
5-16:⚠️ Potential issue | 🟠 MajorUse
TextareaHTMLAttributesto expose textarea-specific props in the type interface.At line 9,
HTMLAttributes<HTMLTextAreaElement>omits textarea-specific props (rows,cols,maxLength,wrap,defaultValue, etc.) from the public type surface. Although the component spreads...propsand accepts these attributes at runtime, the TypeScript interface doesn't reflect that capability. UseReact.TextareaHTMLAttributes<HTMLTextAreaElement>(the conventional React type for textarea elements) to provide accurate typing.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/raystack/components/text-area/text-area.tsx` around lines 5 - 16, The TextAreaProps interface currently extends PropsWithChildren<HTMLAttributes<HTMLTextAreaElement>> which omits textarea-specific attributes; update it to extend PropsWithChildren<React.TextareaHTMLAttributes<HTMLTextAreaElement>> so props like rows, cols, maxLength, wrap, defaultValue, etc. are properly typed; modify the TextAreaProps declaration (the interface named TextAreaProps) and any import usage to use React.TextareaHTMLAttributes<HTMLTextAreaElement> instead of HTMLAttributes<HTMLTextAreaElement>.packages/raystack/components/calendar/date-picker.tsx (1)
174-189:⚠️ Potential issue | 🟠 MajorInvalid visual state is not applied after switching to
aria-invalid.Line 178 sets
aria-invalid, butInputFieldinvalid styling is keyed off wrapperdata-invalid(packages/raystack/components/input-field/input-field.module.css), and the wrapper never receives that attribute (packages/raystack/components/input-field/input-field.tsx). Result: error border/state won’t render for DatePicker.💡 Proposed fix at root cause (`InputField`)
--- a/packages/raystack/components/input-field/input-field.tsx +++ b/packages/raystack/components/input-field/input-field.tsx @@ export const InputField = forwardRef<HTMLInputElement, InputFieldProps>( ( { @@ containerRef, ...props }, ref ) => { + const isInvalid = + props['aria-invalid'] === true || props['aria-invalid'] === 'true'; + return ( <div className={cx( inputWrapper({ size, variant }), disabled && styles['input-disabled-wrapper'], chips?.length && styles['has-chips'] )} + data-invalid={isInvalid ? '' : undefined} style={{ width: width || '100%' }} ref={containerRef} >🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/raystack/components/calendar/date-picker.tsx` around lines 174 - 189, The DatePicker sets aria-invalid on the InputField but InputField's wrapper uses data-invalid for styling and never receives it, so the invalid visual state doesn't render; fix by updating the InputField component (input-field.tsx / InputField) to map/forward the aria-invalid prop to the wrapper as data-invalid (e.g., compute const isInvalid = props['aria-invalid'] || props.error and add data-invalid={isInvalid} on the wrapper element) and ensure the InputField prop types accept aria-invalid so DatePicker's usage continues to work.
🧹 Nitpick comments (11)
apps/www/src/content/docs/components/form/props.ts (1)
19-22: Preferunknownoverany/objectin public docs types.Using
Record<string, any>andobjectweakens type safety in the generated API docs. ConsiderRecord<string, unknown>andRecord<string, unknown>(or a named event-details type).Suggested refinement
onFormSubmit?: ( - formValues: Record<string, any>, - eventDetails: object + formValues: Record<string, unknown>, + eventDetails: Record<string, unknown> ) => void;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/www/src/content/docs/components/form/props.ts` around lines 19 - 22, The onFormSubmit type weakly uses Record<string, any> and object; change it to use stronger types such as Record<string, unknown> for formValues and a structured type for eventDetails (e.g., Record<string, unknown> or a named interface) so the public docs show precise types — update the onFormSubmit signature to accept formValues: Record<string, unknown> and eventDetails: Record<string, unknown> (or replace eventDetails with a named EventDetails type) where the onFormSubmit declaration is defined.packages/raystack/index.tsx (1)
36-40: Consider re-exporting the new props types from root as well.Since
./components/field,./components/fieldset, and./components/formexpose*Props, adding root type re-exports would make top-level imports consistent for consumers.Suggested addition
export { Field } from './components/field'; +export type { FieldProps } from './components/field'; export { Fieldset } from './components/fieldset'; +export type { FieldsetProps } from './components/fieldset'; export { Form } from './components/form'; +export type { FormProps } from './components/form';🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/raystack/index.tsx` around lines 36 - 40, The root re-exports currently export components Field, Fieldset, FilterChip, Flex, and Form but not their prop types; add type re-exports for the props (e.g., FieldProps from './components/field', FieldsetProps from './components/fieldset', and FormProps from './components/form') alongside the existing component exports so consumers can import both components and their types from the package root (keep existing symbol names: Field, Fieldset, Form and add corresponding FieldProps, FieldsetProps, FormProps).packages/raystack/components/text-area/text-area.module.css (1)
62-68: Consolidate disabled styling into one selector.Line 62 duplicates the same disabled declarations already defined in
.textarea:disabled(Line 38+). Keeping both increases drift risk; prefer a single source of truth.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/raystack/components/text-area/text-area.module.css` around lines 62 - 68, Duplicate disabled styles exist in the .disabled rule and .textarea:disabled; remove the .disabled block and consolidate the styles into a single source of truth by keeping the .textarea:disabled selector (or replace it with a grouped selector ".textarea:disabled, .disabled" if you must preserve runtime .disabled class usages), and update any markup that relied on the .disabled class to use the disabled attribute on the textarea component so styling remains identical.apps/www/src/components/playground/form-examples.tsx (1)
24-36: Addnameattributes to form controls in the example.This demo currently omits
name, which makes form-value mapping and server-error association less representative of real usage.♻️ Suggested update
<Fieldset legend='Personal Information'> <Field label='First Name' required> - <InputField placeholder='John' /> + <InputField name='firstName' placeholder='John' /> </Field> <Field label='Last Name' required> - <InputField placeholder='Doe' /> + <InputField name='lastName' placeholder='Doe' /> </Field> </Fieldset> <Field label='Email' required helperText="We'll send a confirmation"> - <InputField type='email' placeholder='john@example.com' /> + <InputField name='email' type='email' placeholder='john@example.com' /> </Field> <Field label='Message' optional> - <TextArea placeholder='Tell us more...' /> + <TextArea name='message' placeholder='Tell us more...' /> </Field>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/www/src/components/playground/form-examples.tsx` around lines 24 - 36, The example form controls (InputField and TextArea inside Field/Fieldset) are missing name attributes; update each form control to include descriptive name props so form libraries and server errors map correctly — e.g. on the InputField in the Field labeled "First Name" add name="firstName", the "Last Name" InputField add name="lastName", the "Email" InputField add name="email", and the "Message" TextArea add name="message"; keep the existing placeholders, types and other props intact so only the name prop is introduced on InputField and TextArea components.apps/www/src/content/docs/components/textarea/index.mdx (1)
32-34: Make the example snippet self-contained.
error={errors.bio?.message}references an undeclared symbol in this snippet; consider a literal error string or include the surrounding state snippet.📝 Minimal self-contained snippet option
-<Field label="Bio" helperText="Tell us about yourself" error={errors.bio?.message}> +<Field label="Bio" helperText="Tell us about yourself" error="Bio is required"> <TextArea placeholder="Write something..." /> </Field>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/www/src/content/docs/components/textarea/index.mdx` around lines 32 - 34, The example uses an undeclared symbol errors.bio?.message which breaks the snippet; either make the snippet self-contained by replacing error={errors.bio?.message} with a literal (e.g., error="This field is required") or include the minimal surrounding form state that declares errors (for example showing useForm/useState and an errors object) so the Field component's error prop has a defined source; update the snippet referencing Field and TextArea accordingly.packages/raystack/components/fieldset/__tests__/fieldset.test.tsx (1)
68-72: Make the “no legend” assertion implementation-agnostic.Line 71 currently checks only
[role="presentation"], which can miss regressions if legend markup changes. Assert absence of actual legend output instead of a single role selector.Proposed test hardening
- expect( - container.querySelector('[role="presentation"]') - ).not.toBeInTheDocument(); + expect(container.querySelector('legend')).not.toBeInTheDocument(); + expect(screen.queryByRole('presentation')).not.toBeInTheDocument();🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/raystack/components/fieldset/__tests__/fieldset.test.tsx` around lines 68 - 72, The test currently checks for a specific role selector ([role="presentation"]) which is implementation-specific; instead update the "does not render legend when not provided" test to assert absence of an actual <legend> element (e.g., use container.querySelector('legend') or RTL's screen.queryByRole/queryByText targeting the legend element) so it fails only if a legend node is rendered; change the assertion in the Fieldset test to check for no legend element rather than the role selector.packages/raystack/components/field/field.module.css (1)
78-81: Strengthen keyboard focus visibility.Current focus styling relies mainly on border color. Add a
:focus-visiblering to improve perceivability for keyboard users.Suggested CSS tweak
.input:focus { border-color: var(--rs-color-border-accent-emphasis); background-color: var(--rs-color-background-base-primary); } + +.input:focus-visible { + outline: 2px solid var(--rs-color-border-accent-emphasis); + outline-offset: 1px; +}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/raystack/components/field/field.module.css` around lines 78 - 81, The current .input:focus rule only changes border and background which can be hard to perceive; add a .input:focus-visible rule that applies a clear visible ring (e.g., an outline or box-shadow using a high-contrast color variable like --rs-color-border-accent-emphasis or a dedicated --rs-color-focus) while preserving existing border/background changes so keyboard users get a stronger visual cue; update the CSS by adding .input:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--rs-color-focus, var(--rs-color-border-accent-emphasis)); } (and keep .input:focus as-is) to target keyboard focus specifically.apps/www/src/content/docs/components/field/index.mdx (1)
138-140: Prefer behavior-focused accessibility wording.These lines lock docs to specific attribute internals. Consider phrasing as “automatically associates label/description/error with the control” to reduce future doc drift.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/www/src/content/docs/components/field/index.mdx` around lines 138 - 140, Update the three doc lines to use behavior-focused accessibility wording instead of specific ARIA attribute names: change the `Field.Label` line to say it "automatically associates the label with the control", `Field.Description` to "automatically associates the description with the control", and `Field.Error` to "automatically associates error messages with the control", referencing the `Field.Label`, `Field.Description`, and `Field.Error` symbols so the docs describe behavior rather than specific attributes like `aria-labelledby`/`aria-describedby`.packages/raystack/components/field/__tests__/field.test.tsx (1)
30-34: Ref forwarding test is too weak; assert the forwarded element instance.At Line 33,
toHaveBeenCalled()can pass even if onlynullwas passed. Prefer checkingref.currentwithcreateRef<HTMLDivElement>().🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/raystack/components/field/__tests__/field.test.tsx` around lines 30 - 34, The ref-forwarding test for Field is too weak; replace the mock ref with a real React ref (e.g., createRef<HTMLDivElement>()) and after render assert the forwarded element instance instead of just that the ref was called. Specifically, in the test for Field, use createRef to create ref, render(<Field ref={ref}>...), then assert ref.current is not null and is the expected element (e.g., expect(ref.current).toBeInstanceOf(HTMLDivElement) or check tagName) to verify proper DOM ref forwarding.apps/www/src/content/docs/components/field/demo.ts (1)
6-15: Avoid forwarding mixedrestprops directly toInputFieldin snippet generation.At Lines 6–15,
restis blindly serialized into<InputField />. Explicitly separate Field-level props from InputField props to prevent invalid code generation as controls evolve.✅ Suggested refactor
-export const getCode = (props: any) => { - const { label, helperText, error, required, optional, ...rest } = props; +export const getCode = (props: any) => { + const { + label, + helperText, + error, + required, + optional, + width, + className, + ...inputProps + } = props; const fieldProps: Record<string, unknown> = {}; if (label) fieldProps.label = label; if (helperText) fieldProps.helperText = helperText; if (error) fieldProps.error = error; if (required) fieldProps.required = required; if (optional) fieldProps.optional = optional; + if (width !== undefined) fieldProps.width = width; + if (className) fieldProps.className = className; return `<Field${getPropsString(fieldProps)}> - <InputField${getPropsString(rest)} placeholder="Enter text" /> + <InputField${getPropsString(inputProps)} placeholder="Enter text" /> </Field>`; };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/www/src/content/docs/components/field/demo.ts` around lines 6 - 15, The snippet currently forwards the entire rest object into InputField which can include Field-level props (label, helperText, error, required, optional) and produce invalid generated code; update the demo to explicitly separate Field props from InputField props by building fieldProps from props.label/helperText/error/required/optional (as now) and building inputProps by copying rest but removing those Field keys before calling getPropsString; use getPropsString(fieldProps) for <Field> and getPropsString(inputProps) for <InputField> so only input-relevant props are serialized.packages/raystack/components/field/field.tsx (1)
40-40: Use nullish coalescing forwidthdefault.Using
||meanswidth={0}orwidth=""will fallback to'100%'. If a consumer explicitly passes0, they likely expect0, not100%. Prefer??for a more predictable default.♻️ Suggested fix
- style={{ width: width || '100%' }} + style={{ width: width ?? '100%' }}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/raystack/components/field/field.tsx` at line 40, The inline style default for width uses the logical OR operator which treats falsy values like 0 or empty string as absent; update the style expression in the Field component (the element with style={{ width: width || '100%' }}) to use the nullish coalescing operator so an explicit 0 or "" is preserved (i.e., change the width fallback from "width || '100%'" to "width ?? '100%'" in the Field component).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/www/src/components/playground/text-area-examples.tsx`:
- Line 16: The standalone TextArea example renders a control without an
accessible name; update the example that uses the TextArea component so it has
an explicit accessible label by either wrapping it in your Field/Label
components (e.g., add a Field with a Label and put TextArea inside) or by adding
an aria-label (or aria-labelledby) prop directly to TextArea; ensure the label
text is descriptive and not just placeholder content so screen readers can
announce the control.
In `@apps/www/src/content/docs/components/field/props.ts`:
- Around line 29-33: The JSDoc for the validate prop is outdated: update the
comment above the validate function to reflect its actual signature by
mentioning that it may return a string, an array of strings, null, or a Promise
resolving to string | string[] | null; modify the comment line "Custom
validation function. Return an error string or null." to something like "Custom
validation function. Return a string, an array of strings, null, or a Promise
resolving to string | string[] | null." so it matches the validate?: (value:
unknown) => string | string[] | null | Promise<string | string[] | null>;
declaration.
In `@apps/www/src/content/docs/components/fieldset/index.mdx`:
- Around line 60-61: Update the inaccurate description for Fieldset.Legend:
change the text that currently says it "renders a <div> element with
role='presentation'" to state that Fieldset.Legend renders a native <legend>
element (the semantic, accessible element for fieldset legends). Edit the
description near the Fieldset.Legend docs so it matches the implementation and
the later correct note (line 93) that the legend provides the accessible name
for the group.
In `@packages/raystack/components/field/field.tsx`:
- Around line 12-13: The required and optional props on FieldProps/Field can
conflict (both true) causing contradictory UI; make them mutually exclusive or
enforce runtime precedence: update the prop type (FieldProps) to a discriminated
union so required and optional cannot both be provided, or add runtime logic
inside the Field component (e.g., compute const isRequired = props.required ===
true; const isOptional = !isRequired && props.optional === true) and use
isOptional/isRequired when rendering the label so required wins and the optional
marker is suppressed when required is true.
In `@packages/raystack/components/fieldset/__tests__/fieldset.test.tsx`:
- Around line 99-107: The test "sets data-disabled attribute when disabled" only
asserts the data attribute but not native disabled behavior; update this test in
packages/raystack/components/fieldset/__tests__/fieldset.test.tsx (the it block
that renders <Fieldset disabled>...) to also assert that native disabled
semantics are propagated: check that the rendered <fieldset> element has
fieldset.disabled === true and that the child input
(querySelector('[data-testid="child-input"]')) has the disabled attribute (or
disabled property true) and is not focusable/interactable (e.g.,
document.activeElement cannot be the input after a focus attempt). This ensures
both the data attribute and native disabled behavior on Fieldset and its
children are covered.
In `@packages/raystack/components/fieldset/fieldset.tsx`:
- Around line 21-27: The current JSX uses a truthy check `legend &&
<FieldsetPrimitive.Legend ...>` which hides valid ReactNode values like 0;
change this to an explicit null/undefined check so non-null values render.
Locate the render block using FieldsetPrimitive.Legend (and props
legendClassName, styles.legend) in fieldset.tsx and replace the conditional with
a check such as `legend != null` (or `legend !== null && legend !== undefined`)
so numbers and other valid ReactNode values are rendered while still excluding
null/undefined.
- Around line 34-40: The ref type for the FieldsetLegend forwardRef is
incorrect: change the generic from HTMLDivElement to HTMLLegendElement so the
ref matches the rendered <legend> element; update the forwardRef declaration for
FieldsetLegend (and any related type annotations referencing HTMLDivElement) to
HTMLLegendElement while keeping the component body using
FieldsetPrimitive.Legend and cx(styles.legend, className) unchanged.
In `@packages/raystack/components/input-field/input-field.module.css`:
- Around line 28-33: Add an "invalid" boolean prop to InputFieldProps and have
the InputField component set the data-invalid attribute on the wrapper when
invalid is true so the existing .inputWrapper[data-invalid] styles are applied;
update the component's prop destructuring/defaults to include invalid (default
false) and ensure the wrapper element (the one that currently uses className
'inputWrapper') receives data-invalid={invalid} and that the prop is forwarded
where needed and reflected in types/interfaces (InputFieldProps, InputField).
In `@packages/raystack/components/input-field/input-field.tsx`:
- Around line 68-76: The wrapper div for the InputField component (the element
using inputWrapper({ size, variant }) and ref containerRef) never sets the
data-invalid attribute, so the CSS selector .inputWrapper[data-invalid] is never
triggered; update the wrapper rendering in input-field.tsx (both places around
the containerRef usage, including the similar block at lines ~99-113) to add
data-invalid={!!invalid} (or data-invalid when invalid) so the existing
.inputWrapper[data-invalid] styles apply when the component's invalid prop/state
is true.
---
Outside diff comments:
In `@packages/raystack/components/calendar/date-picker.tsx`:
- Around line 174-189: The DatePicker sets aria-invalid on the InputField but
InputField's wrapper uses data-invalid for styling and never receives it, so the
invalid visual state doesn't render; fix by updating the InputField component
(input-field.tsx / InputField) to map/forward the aria-invalid prop to the
wrapper as data-invalid (e.g., compute const isInvalid = props['aria-invalid']
|| props.error and add data-invalid={isInvalid} on the wrapper element) and
ensure the InputField prop types accept aria-invalid so DatePicker's usage
continues to work.
In `@packages/raystack/components/text-area/text-area.tsx`:
- Line 24: The component sets a default width prop that ends up overriding any
consumer-provided style.width because the JSX merges props using "{ ...style,
width }"; update the merge so consumer styles win by placing the default width
first and then spreading style (e.g., change the merge in the TextArea component
where style and width are combined to use width before spreading style) or
conditionally apply width only when style.width is not provided; locate this in
the TextArea function/props handling to adjust the object merge order for style
and width.
- Around line 9-16: TextAreaProps no longer declares a label prop but callers
(e.g., the example page that passes label to TextArea) still rely on it; either
restore the label prop on the component or remove those usages. To fix, add an
optional label?: React.ReactNode (or string) to the TextAreaProps interface and
update the TextArea component to accept and render that label (e.g., render
label above the textarea when label is provided), or alternatively remove the
label prop usage where TextArea is instantiated. Update references to
TextAreaProps and the TextArea component accordingly so the types and runtime UI
stay in sync.
- Around line 5-16: The TextAreaProps interface currently extends
PropsWithChildren<HTMLAttributes<HTMLTextAreaElement>> which omits
textarea-specific attributes; update it to extend
PropsWithChildren<React.TextareaHTMLAttributes<HTMLTextAreaElement>> so props
like rows, cols, maxLength, wrap, defaultValue, etc. are properly typed; modify
the TextAreaProps declaration (the interface named TextAreaProps) and any import
usage to use React.TextareaHTMLAttributes<HTMLTextAreaElement> instead of
HTMLAttributes<HTMLTextAreaElement>.
---
Nitpick comments:
In `@apps/www/src/components/playground/form-examples.tsx`:
- Around line 24-36: The example form controls (InputField and TextArea inside
Field/Fieldset) are missing name attributes; update each form control to include
descriptive name props so form libraries and server errors map correctly — e.g.
on the InputField in the Field labeled "First Name" add name="firstName", the
"Last Name" InputField add name="lastName", the "Email" InputField add
name="email", and the "Message" TextArea add name="message"; keep the existing
placeholders, types and other props intact so only the name prop is introduced
on InputField and TextArea components.
In `@apps/www/src/content/docs/components/field/demo.ts`:
- Around line 6-15: The snippet currently forwards the entire rest object into
InputField which can include Field-level props (label, helperText, error,
required, optional) and produce invalid generated code; update the demo to
explicitly separate Field props from InputField props by building fieldProps
from props.label/helperText/error/required/optional (as now) and building
inputProps by copying rest but removing those Field keys before calling
getPropsString; use getPropsString(fieldProps) for <Field> and
getPropsString(inputProps) for <InputField> so only input-relevant props are
serialized.
In `@apps/www/src/content/docs/components/field/index.mdx`:
- Around line 138-140: Update the three doc lines to use behavior-focused
accessibility wording instead of specific ARIA attribute names: change the
`Field.Label` line to say it "automatically associates the label with the
control", `Field.Description` to "automatically associates the description with
the control", and `Field.Error` to "automatically associates error messages with
the control", referencing the `Field.Label`, `Field.Description`, and
`Field.Error` symbols so the docs describe behavior rather than specific
attributes like `aria-labelledby`/`aria-describedby`.
In `@apps/www/src/content/docs/components/form/props.ts`:
- Around line 19-22: The onFormSubmit type weakly uses Record<string, any> and
object; change it to use stronger types such as Record<string, unknown> for
formValues and a structured type for eventDetails (e.g., Record<string, unknown>
or a named interface) so the public docs show precise types — update the
onFormSubmit signature to accept formValues: Record<string, unknown> and
eventDetails: Record<string, unknown> (or replace eventDetails with a named
EventDetails type) where the onFormSubmit declaration is defined.
In `@apps/www/src/content/docs/components/textarea/index.mdx`:
- Around line 32-34: The example uses an undeclared symbol errors.bio?.message
which breaks the snippet; either make the snippet self-contained by replacing
error={errors.bio?.message} with a literal (e.g., error="This field is
required") or include the minimal surrounding form state that declares errors
(for example showing useForm/useState and an errors object) so the Field
component's error prop has a defined source; update the snippet referencing
Field and TextArea accordingly.
In `@packages/raystack/components/field/__tests__/field.test.tsx`:
- Around line 30-34: The ref-forwarding test for Field is too weak; replace the
mock ref with a real React ref (e.g., createRef<HTMLDivElement>()) and after
render assert the forwarded element instance instead of just that the ref was
called. Specifically, in the test for Field, use createRef to create ref,
render(<Field ref={ref}>...), then assert ref.current is not null and is the
expected element (e.g., expect(ref.current).toBeInstanceOf(HTMLDivElement) or
check tagName) to verify proper DOM ref forwarding.
In `@packages/raystack/components/field/field.module.css`:
- Around line 78-81: The current .input:focus rule only changes border and
background which can be hard to perceive; add a .input:focus-visible rule that
applies a clear visible ring (e.g., an outline or box-shadow using a
high-contrast color variable like --rs-color-border-accent-emphasis or a
dedicated --rs-color-focus) while preserving existing border/background changes
so keyboard users get a stronger visual cue; update the CSS by adding
.input:focus-visible { outline: none; box-shadow: 0 0 0 3px
var(--rs-color-focus, var(--rs-color-border-accent-emphasis)); } (and keep
.input:focus as-is) to target keyboard focus specifically.
In `@packages/raystack/components/field/field.tsx`:
- Line 40: The inline style default for width uses the logical OR operator which
treats falsy values like 0 or empty string as absent; update the style
expression in the Field component (the element with style={{ width: width ||
'100%' }}) to use the nullish coalescing operator so an explicit 0 or "" is
preserved (i.e., change the width fallback from "width || '100%'" to "width ??
'100%'" in the Field component).
In `@packages/raystack/components/fieldset/__tests__/fieldset.test.tsx`:
- Around line 68-72: The test currently checks for a specific role selector
([role="presentation"]) which is implementation-specific; instead update the
"does not render legend when not provided" test to assert absence of an actual
<legend> element (e.g., use container.querySelector('legend') or RTL's
screen.queryByRole/queryByText targeting the legend element) so it fails only if
a legend node is rendered; change the assertion in the Fieldset test to check
for no legend element rather than the role selector.
In `@packages/raystack/components/text-area/text-area.module.css`:
- Around line 62-68: Duplicate disabled styles exist in the .disabled rule and
.textarea:disabled; remove the .disabled block and consolidate the styles into a
single source of truth by keeping the .textarea:disabled selector (or replace it
with a grouped selector ".textarea:disabled, .disabled" if you must preserve
runtime .disabled class usages), and update any markup that relied on the
.disabled class to use the disabled attribute on the textarea component so
styling remains identical.
In `@packages/raystack/index.tsx`:
- Around line 36-40: The root re-exports currently export components Field,
Fieldset, FilterChip, Flex, and Form but not their prop types; add type
re-exports for the props (e.g., FieldProps from './components/field',
FieldsetProps from './components/fieldset', and FormProps from
'./components/form') alongside the existing component exports so consumers can
import both components and their types from the package root (keep existing
symbol names: Field, Fieldset, Form and add corresponding FieldProps,
FieldsetProps, FormProps).
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: f6a4a6e4-c7b2-4371-b489-cae600196284
📒 Files selected for processing (41)
apps/www/src/components/playground/field-examples.tsxapps/www/src/components/playground/fieldset-examples.tsxapps/www/src/components/playground/form-examples.tsxapps/www/src/components/playground/index.tsapps/www/src/components/playground/input-field-examples.tsxapps/www/src/components/playground/text-area-examples.tsxapps/www/src/content/docs/components/field/demo.tsapps/www/src/content/docs/components/field/index.mdxapps/www/src/content/docs/components/field/props.tsapps/www/src/content/docs/components/fieldset/demo.tsapps/www/src/content/docs/components/fieldset/index.mdxapps/www/src/content/docs/components/fieldset/props.tsapps/www/src/content/docs/components/form/demo.tsapps/www/src/content/docs/components/form/index.mdxapps/www/src/content/docs/components/form/props.tsapps/www/src/content/docs/components/input-field/demo.tsapps/www/src/content/docs/components/input-field/index.mdxapps/www/src/content/docs/components/input-field/props.tsapps/www/src/content/docs/components/textarea/demo.tsapps/www/src/content/docs/components/textarea/index.mdxapps/www/src/content/docs/components/textarea/props.tspackages/raystack/components/calendar/date-picker.tsxpackages/raystack/components/field/__tests__/field.test.tsxpackages/raystack/components/field/field.module.csspackages/raystack/components/field/field.tsxpackages/raystack/components/field/index.tsxpackages/raystack/components/fieldset/__tests__/fieldset.test.tsxpackages/raystack/components/fieldset/fieldset.module.csspackages/raystack/components/fieldset/fieldset.tsxpackages/raystack/components/fieldset/index.tsxpackages/raystack/components/form/__tests__/form.test.tsxpackages/raystack/components/form/form.module.csspackages/raystack/components/form/form.tsxpackages/raystack/components/form/index.tsxpackages/raystack/components/input-field/__tests__/input-field.test.tsxpackages/raystack/components/input-field/input-field.module.csspackages/raystack/components/input-field/input-field.tsxpackages/raystack/components/text-area/__tests__/text-area.test.tsxpackages/raystack/components/text-area/text-area.module.csspackages/raystack/components/text-area/text-area.tsxpackages/raystack/index.tsx
| <Field label='Error TextArea' error='This field has an error'> | ||
| <TextArea placeholder='Enter your text here' /> | ||
| </Field> | ||
| <TextArea placeholder='Without Field wrapper' width='300px' /> |
There was a problem hiding this comment.
Standalone TextArea example is missing an accessible name.
Line 16 renders a control without Field label or aria-label. Placeholder text is not a reliable accessible label.
✅ Suggested fix
- <TextArea placeholder='Without Field wrapper' width='300px' />
+ <TextArea
+ placeholder='Without Field wrapper'
+ aria-label='Without Field wrapper'
+ width='300px'
+ />📝 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.
| <TextArea placeholder='Without Field wrapper' width='300px' /> | |
| <TextArea | |
| placeholder='Without Field wrapper' | |
| aria-label='Without Field wrapper' | |
| width='300px' | |
| /> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/www/src/components/playground/text-area-examples.tsx` at line 16, The
standalone TextArea example renders a control without an accessible name; update
the example that uses the TextArea component so it has an explicit accessible
label by either wrapping it in your Field/Label components (e.g., add a Field
with a Label and put TextArea inside) or by adding an aria-label (or
aria-labelledby) prop directly to TextArea; ensure the label text is descriptive
and not just placeholder content so screen readers can announce the control.
| /** Custom validation function. Return an error string or null. */ | ||
| validate?: ( | ||
| value: unknown | ||
| ) => string | string[] | null | Promise<string | string[] | null>; | ||
|
|
There was a problem hiding this comment.
validate JSDoc is out of sync with its declared return type.
At Line 29, the comment says “error string or null”, but Lines 30–32 also allow string[] and Promise. Please align docs text with the actual signature.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/www/src/content/docs/components/field/props.ts` around lines 29 - 33,
The JSDoc for the validate prop is outdated: update the comment above the
validate function to reflect its actual signature by mentioning that it may
return a string, an array of strings, null, or a Promise resolving to string |
string[] | null; modify the comment line "Custom validation function. Return an
error string or null." to something like "Custom validation function. Return a
string, an array of strings, null, or a Promise resolving to string | string[] |
null." so it matches the validate?: (value: unknown) => string | string[] | null
| Promise<string | string[] | null>; declaration.
| An accessible legend for the fieldset. Renders a `<div>` element with `role="presentation"`. | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Verify what Fieldset.Legend renders and how accessible naming is wired.
fd 'fieldset\.tsx$' | while read -r f; do
echo "=== $f ==="
rg -n -C3 'Fieldset\.Legend|legend|role=.presentation.|aria-labelledby|<legend' "$f"
doneRepository: raystack/apsara
Length of output: 1144
🏁 Script executed:
# Read the documentation file to see lines 60-61 and 93-94 in context
cat -n apps/www/src/content/docs/components/fieldset/index.mdx | sed -n '50,105p'Repository: raystack/apsara
Length of output: 1342
🏁 Script executed:
# Find the FieldsetPrimitive import to understand the source
rg -n 'FieldsetPrimitive|from.*fieldset' packages/raystack/components/fieldset/Repository: raystack/apsara
Length of output: 1326
🏁 Script executed:
# Search for aria-labelledby or accessible naming patterns in fieldset implementation
rg -n 'aria-labelledby|aria-label|aria-describedby|labelled|named' packages/raystack/components/fieldset/Repository: raystack/apsara
Length of output: 41
🏁 Script executed:
# Check Base UI's Fieldset.Legend rendering behavior
rg -n 'role=.presentation' packages/raystack/components/fieldset/ -A2 -B2Repository: raystack/apsara
Length of output: 581
🏁 Script executed:
# Search for any CSS or styling that might affect the rendered element
cat packages/raystack/components/fieldset/fieldset.module.cssRepository: raystack/apsara
Length of output: 475
🌐 Web query:
Base UI @base-ui/react/fieldset Legend component rendered element
💡 Result:
The Legend component from @base-ui/react/fieldset renders a <legend> HTML element.
Citations:
Fix inaccurate element description on line 60.
Line 60 incorrectly states that Fieldset.Legend renders a <div> element with role="presentation". Base UI's Legend component actually renders a native <legend> element, which is the correct semantic element for fieldset legends and provides the accessible name for the group (confirming line 93 is accurate). Update line 60 to reflect the correct rendered element.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/www/src/content/docs/components/fieldset/index.mdx` around lines 60 -
61, Update the inaccurate description for Fieldset.Legend: change the text that
currently says it "renders a <div> element with role='presentation'" to state
that Fieldset.Legend renders a native <legend> element (the semantic, accessible
element for fieldset legends). Edit the description near the Fieldset.Legend
docs so it matches the implementation and the later correct note (line 93) that
the legend provides the accessible name for the group.
| required?: boolean; | ||
| optional?: boolean; |
There was a problem hiding this comment.
required and optional props can conflict.
Both props can be true simultaneously, causing the label to display both * and (optional) text—a contradictory UX. Consider making them mutually exclusive or having one take precedence.
🛡️ Option: Add runtime precedence (required wins)
{label && (
<FieldPrimitive.Label className={styles.label}>
{label}
{required && (
<span className={styles['required-indicator']} aria-hidden='true'>
*
</span>
)}
- {optional && <span className={styles.optional}>(optional)</span>}
+ {optional && !required && <span className={styles.optional}>(optional)</span>}
</FieldPrimitive.Label>
)}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/raystack/components/field/field.tsx` around lines 12 - 13, The
required and optional props on FieldProps/Field can conflict (both true) causing
contradictory UI; make them mutually exclusive or enforce runtime precedence:
update the prop type (FieldProps) to a discriminated union so required and
optional cannot both be provided, or add runtime logic inside the Field
component (e.g., compute const isRequired = props.required === true; const
isOptional = !isRequired && props.optional === true) and use
isOptional/isRequired when rendering the label so required wins and the optional
marker is suppressed when required is true.
| it('sets data-disabled attribute when disabled', () => { | ||
| const { container } = render( | ||
| <Fieldset disabled> | ||
| <input data-testid='child-input' /> | ||
| </Fieldset> | ||
| ); | ||
| const fieldset = container.querySelector('fieldset'); | ||
| expect(fieldset).toHaveAttribute('data-disabled', ''); | ||
| }); |
There was a problem hiding this comment.
Cover native disabled semantics, not only data attributes.
Line 106 verifies styling state (data-disabled) but not behavior. Add assertions for native disabled propagation so this contract can’t regress silently.
Suggested coverage addition
const fieldset = container.querySelector('fieldset');
expect(fieldset).toHaveAttribute('data-disabled', '');
+ expect(fieldset).toHaveAttribute('disabled');
+ expect(screen.getByTestId('child-input')).toBeDisabled();🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/raystack/components/fieldset/__tests__/fieldset.test.tsx` around
lines 99 - 107, The test "sets data-disabled attribute when disabled" only
asserts the data attribute but not native disabled behavior; update this test in
packages/raystack/components/fieldset/__tests__/fieldset.test.tsx (the it block
that renders <Fieldset disabled>...) to also assert that native disabled
semantics are propagated: check that the rendered <fieldset> element has
fieldset.disabled === true and that the child input
(querySelector('[data-testid="child-input"]')) has the disabled attribute (or
disabled property true) and is not focusable/interactable (e.g.,
document.activeElement cannot be the input after a focus attempt). This ensures
both the data attribute and native disabled behavior on Fieldset and its
children are covered.
| {legend && ( | ||
| <FieldsetPrimitive.Legend | ||
| className={cx(styles.legend, legendClassName)} | ||
| > | ||
| {legend} | ||
| </FieldsetPrimitive.Legend> | ||
| )} |
There was a problem hiding this comment.
Render legend for non-null values instead of truthy-only checks.
At Line 21, legend && suppresses valid ReactNode values like 0. Use a null/undefined check instead.
✅ Suggested fix
- {legend && (
+ {legend !== undefined && legend !== null && (
<FieldsetPrimitive.Legend
className={cx(styles.legend, legendClassName)}
>
{legend}
</FieldsetPrimitive.Legend>
)}📝 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.
| {legend && ( | |
| <FieldsetPrimitive.Legend | |
| className={cx(styles.legend, legendClassName)} | |
| > | |
| {legend} | |
| </FieldsetPrimitive.Legend> | |
| )} | |
| {legend !== undefined && legend !== null && ( | |
| <FieldsetPrimitive.Legend | |
| className={cx(styles.legend, legendClassName)} | |
| > | |
| {legend} | |
| </FieldsetPrimitive.Legend> | |
| )} |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/raystack/components/fieldset/fieldset.tsx` around lines 21 - 27, The
current JSX uses a truthy check `legend && <FieldsetPrimitive.Legend ...>` which
hides valid ReactNode values like 0; change this to an explicit null/undefined
check so non-null values render. Locate the render block using
FieldsetPrimitive.Legend (and props legendClassName, styles.legend) in
fieldset.tsx and replace the conditional with a check such as `legend != null`
(or `legend !== null && legend !== undefined`) so numbers and other valid
ReactNode values are rendered while still excluding null/undefined.
| const FieldsetLegend = forwardRef< | ||
| HTMLDivElement, | ||
| FieldsetPrimitive.Legend.Props | ||
| >(({ className, ...props }, ref) => ( | ||
| <FieldsetPrimitive.Legend | ||
| ref={ref} | ||
| className={cx(styles.legend, className)} |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
In @base-ui/react/fieldset, what DOM element type does Fieldset.Legend render, and what ref type should be used with React.forwardRef for it?
💡 Result:
In @base-ui/react/fieldset, Fieldset.Legend renders a <legend> DOM element. For React.forwardRef, use React.RefObject<HTMLLegendElement> or React.Ref<HTMLLegendElement> (equivalent to React.LegacyRef<HTMLLegendElement>), as it is a standard HTML element ref.
Citations:
Change ref type from HTMLDivElement to HTMLLegendElement.
Fieldset.Legend renders a <legend> element, not a div. The ref type must be HTMLLegendElement to correctly reflect the underlying DOM element.
Fix
const FieldsetLegend = forwardRef<
- HTMLDivElement,
+ HTMLLegendElement,
FieldsetPrimitive.Legend.Props
>(({ className, ...props }, ref) => (📝 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.
| const FieldsetLegend = forwardRef< | |
| HTMLDivElement, | |
| FieldsetPrimitive.Legend.Props | |
| >(({ className, ...props }, ref) => ( | |
| <FieldsetPrimitive.Legend | |
| ref={ref} | |
| className={cx(styles.legend, className)} | |
| const FieldsetLegend = forwardRef< | |
| HTMLLegendElement, | |
| FieldsetPrimitive.Legend.Props | |
| >(({ className, ...props }, ref) => ( | |
| <FieldsetPrimitive.Legend | |
| ref={ref} | |
| className={cx(styles.legend, className)} |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/raystack/components/fieldset/fieldset.tsx` around lines 34 - 40, The
ref type for the FieldsetLegend forwardRef is incorrect: change the generic from
HTMLDivElement to HTMLLegendElement so the ref matches the rendered <legend>
element; update the forwardRef declaration for FieldsetLegend (and any related
type annotations referencing HTMLDivElement) to HTMLLegendElement while keeping
the component body using FieldsetPrimitive.Legend and cx(styles.legend,
className) unchanged.
| .inputWrapper[data-invalid] { | ||
| border-color: var(--rs-color-border-danger-primary); | ||
| } | ||
|
|
||
| .input-error-wrapper:hover { | ||
| border-color: var(--rs-color-border-danger-emphasis-hover); | ||
| } | ||
|
|
||
| .input-error-wrapper:focus-within { | ||
| .inputWrapper[data-invalid]:hover, | ||
| .inputWrapper[data-invalid]:focus-within { |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Check Field composition:"
fd '^field\.tsx$' packages | while read -r f; do
echo "--- $f"
rg -n "FieldPrimitive\.(Root|Label|Control|Error|Description)|className=\{styles\.control\}" "$f"
done
echo
echo "Check InputField state-attribute wiring:"
fd '^input-field\.tsx$' packages | while read -r f; do
echo "--- $f"
rg -n "data-invalid|data-disabled|FieldPrimitive\.Control|aria-invalid|invalid" "$f"
doneRepository: raystack/apsara
Length of output: 1145
🏁 Script executed:
# Check if input-field.tsx exists and read its content
fd '^input-field\.tsx$' packages -x cat {} -n
# Also check the full field.tsx to understand the complete structure
echo "=== Full field.tsx context ==="
cat -n packages/raystack/components/field/field.tsx
# Check what's in input-field directory
echo "=== InputField module files ==="
find packages/raystack/components/input-field -type f | head -20Repository: raystack/apsara
Length of output: 8457
🏁 Script executed:
# Search for InputField usage patterns
echo "=== InputField usage patterns ==="
rg -n "InputField|data-invalid" packages/raystack/components --type tsx --type ts -B 2 -A 2 | head -100
# Check if InputField should have an invalid prop based on tests
echo "=== InputField test file ==="
cat packages/raystack/components/input-field/__tests__/input-field.test.tsxRepository: raystack/apsara
Length of output: 9828
🏁 Script executed:
# Read the CSS file to see what [data-invalid] styles exist
echo "=== input-field.module.css content ==="
cat packages/raystack/components/input-field/input-field.module.css
# Search for any usage of InputField in combination with Field
echo -e "\n=== Searching for InputField usage context ==="
rg -r "InputField|Field" packages/raystack/components --type tsx -B 3 -A 3 | grep -A 5 -B 5 "InputField" | head -80Repository: raystack/apsara
Length of output: 5037
🏁 Script executed:
# Search more broadly for InputField integration with Field
echo "=== Search for Field/InputField combinations ==="
rg "Field|InputField" packages/raystack/components --type ts --type jsx -l | head -20
# Check if there are any examples or stories
find packages -name "*.stories.*" -o -name "*.example.*" | head -10
# Look at the Field module to see exported exports and composition
echo -e "\n=== Check field index exports ==="
cat packages/raystack/components/field/index.tsx 2>/dev/null || echo "No index.tsx found"Repository: raystack/apsara
Length of output: 263
[data-invalid] selector in InputField CSS is orphaned—component never sets this attribute.
InputField lacks an invalid prop to activate the defined .inputWrapper[data-invalid] styles (lines 28–33 in input-field.module.css). The component accepts disabled and applies the corresponding class conditionally, but provides no API for invalid state.
Add an invalid prop to InputFieldProps and set data-invalid on the wrapper:
Suggested fix
export interface InputFieldProps
extends Omit<ComponentPropsWithoutRef<'input'>, 'size'>,
VariantProps<typeof inputWrapper> {
disabled?: boolean;
+ invalid?: boolean;
leadingIcon?: ReactNode;export const InputField = forwardRef<HTMLInputElement, InputFieldProps>(
(
{
className,
disabled,
+ invalid,
placeholder, <div
className={cx(
inputWrapper({ size, variant }),
disabled && styles['input-disabled-wrapper'],
chips?.length && styles['has-chips']
)}
+ data-invalid={invalid || undefined}
style={{ width: width || '100%' }}
ref={containerRef}
>Note: Field.tsx using a plain div instead of FieldPrimitive.Control is a separate concern and does not affect InputField, which is a standalone component.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/raystack/components/input-field/input-field.module.css` around lines
28 - 33, Add an "invalid" boolean prop to InputFieldProps and have the
InputField component set the data-invalid attribute on the wrapper when invalid
is true so the existing .inputWrapper[data-invalid] styles are applied; update
the component's prop destructuring/defaults to include invalid (default false)
and ensure the wrapper element (the one that currently uses className
'inputWrapper') receives data-invalid={invalid} and that the prop is forwarded
where needed and reflected in types/interfaces (InputFieldProps, InputField).
| <div | ||
| className={cx( | ||
| inputWrapper({ size, variant }), | ||
| disabled && styles['input-disabled-wrapper'], | ||
| chips?.length && styles['has-chips'] | ||
| )} | ||
| <div | ||
| className={cx( | ||
| inputWrapper({ size, variant, className }), | ||
| error && styles['input-error-wrapper'], | ||
| disabled && styles['input-disabled-wrapper'], | ||
| chips?.length && styles['has-chips'] | ||
| )} | ||
| ref={containerRef} | ||
| > | ||
| {leadingIcon && ( | ||
| <div className={styles['leading-icon']}>{leadingIcon}</div> | ||
| )} | ||
| {prefix && <div className={styles.prefix}>{prefix}</div>} | ||
|
|
||
| <div className={styles['chip-input-container']}> | ||
| {chips?.slice(0, maxChipsVisible).map((chip, index) => ( | ||
| <Chip | ||
| key={index} | ||
| variant='outline' | ||
| isDismissible={!!chip.onRemove} | ||
| onDismiss={chip.onRemove} | ||
| className={styles.chip} | ||
| > | ||
| {chip.label} | ||
| </Chip> | ||
| ))} | ||
| {chips && chips.length > maxChipsVisible && ( | ||
| <span className={styles['chip-overflow']}> | ||
| +{chips.length - maxChipsVisible} | ||
| </span> | ||
| )} | ||
| <input | ||
| ref={ref} | ||
| className={cx( | ||
| styles['input-field'], | ||
| leadingIcon && styles['has-leading-icon'], | ||
| trailingIcon && styles['has-trailing-icon'], | ||
| prefix && styles['has-prefix'], | ||
| suffix && styles['has-suffix'], | ||
| error && styles['input-error'], | ||
| disabled && styles['input-disabled'], | ||
| className | ||
| )} | ||
| aria-invalid={!!error} | ||
| placeholder={placeholder} | ||
| disabled={disabled} | ||
| {...props} | ||
| /> | ||
| </div> | ||
| style={{ width: width || '100%' }} | ||
| ref={containerRef} | ||
| > |
There was a problem hiding this comment.
Invalid border styles are no longer reachable.
packages/raystack/components/input-field/input-field.module.css:28-35 styles .inputWrapper[data-invalid], but Lines 68–76 never set data-invalid on the wrapper. This drops visible error-state styling.
✅ Suggested fix
(
{
className,
disabled,
+ 'aria-invalid': ariaInvalid,
placeholder,
leadingIcon,
trailingIcon,
@@
...props
},
ref
) => {
+ const isInvalid = ariaInvalid === true || ariaInvalid === 'true';
return (
<div
@@
style={{ width: width || '100%' }}
ref={containerRef}
+ data-invalid={isInvalid ? '' : undefined}
>
@@
<input
ref={ref}
@@
placeholder={placeholder}
disabled={disabled}
+ aria-invalid={ariaInvalid}
{...props}
/>Also applies to: 99-113
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/raystack/components/input-field/input-field.tsx` around lines 68 -
76, The wrapper div for the InputField component (the element using
inputWrapper({ size, variant }) and ref containerRef) never sets the
data-invalid attribute, so the CSS selector .inputWrapper[data-invalid] is never
triggered; update the wrapper rendering in input-field.tsx (both places around
the containerRef usage, including the similar block at lines ~99-113) to add
data-invalid={!!invalid} (or data-invalid when invalid) so the existing
.inputWrapper[data-invalid] styles apply when the component's invalid prop/state
is true.
Description
This PR adds new Form components
Summary by CodeRabbit
Release Notes
New Features
Documentation