Skip to content

Commit 4f284f6

Browse files
committed
fix(emcn): let a custom modal field associate its hint with the control it wraps
`ChipModalField` computes `aria-required`/`aria-invalid`/`aria-describedby` from its own state, but `type='custom'` returned its children untouched — so the `hint` and `error` text it renders was visible and never announced. The field cannot apply the ARIA itself here: a custom child may be a bare input or a wrapper several levels above one, which is the same reason `associatesLabel` already excludes custom from the label's `htmlFor`. Adds a function form for custom children that receives the ARIA, so the consumer — which knows where focus lands — attaches it. Existing `ReactNode` children are unaffected; all five current custom-field call sites keep working untouched. Uses it for the Atlassian API token field, whose coverage hint this branch had just relocated onto a custom field.
1 parent 080fce4 commit 4f284f6

2 files changed

Lines changed: 38 additions & 16 deletions

File tree

apps/sim/app/workspace/[workspaceId]/integrations/components/connect-service-account-modal/connect-service-account-modal.tsx

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -504,20 +504,23 @@ function AtlassianServiceAccountModal({
504504
</ChipModalHeader>
505505
<ChipModalBody>
506506
<ChipModalField type='custom' title='API token' required hint={ATLASSIAN_COVERAGE_HINT}>
507-
<SecretInput
508-
value={apiToken}
509-
onChange={(value) => {
510-
setApiToken(value)
511-
if (error) setError(null)
512-
}}
513-
placeholder='Paste API token'
514-
name='atlassian_service_account_api_token'
515-
autoComplete='new-password'
516-
autoCorrect='off'
517-
autoCapitalize='off'
518-
data-lpignore='true'
519-
data-form-type='other'
520-
/>
507+
{(aria) => (
508+
<SecretInput
509+
{...aria}
510+
value={apiToken}
511+
onChange={(value) => {
512+
setApiToken(value)
513+
if (error) setError(null)
514+
}}
515+
placeholder='Paste API token'
516+
name='atlassian_service_account_api_token'
517+
autoComplete='new-password'
518+
autoCorrect='off'
519+
autoCapitalize='off'
520+
data-lpignore='true'
521+
data-form-type='other'
522+
/>
523+
)}
521524
</ChipModalField>
522525

523526
<ChipModalField

packages/emcn/src/components/chip-modal/chip-modal.tsx

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -549,9 +549,28 @@ export interface ChipModalEmailsFieldProps extends ChipModalFieldBaseProps {
549549
placeholder?: string
550550
}
551551

552+
/**
553+
* ARIA the field derives from its own state and renders elsewhere in the row —
554+
* the `hint`/`error` paragraph ids, plus `required`/`invalid` flags.
555+
*/
556+
export interface ChipModalFieldAria {
557+
'aria-required'?: boolean
558+
'aria-invalid'?: boolean
559+
'aria-describedby'?: string
560+
}
561+
552562
interface ChipModalCustomFieldProps extends ChipModalFieldBaseProps {
553563
type: 'custom'
554-
children: React.ReactNode
564+
/**
565+
* Arbitrary JSX, or a function receiving the field's {@link ChipModalFieldAria}.
566+
*
567+
* The owned control types wire this ARIA themselves, but a custom field can
568+
* hold anything — a bare input, or a wrapper several levels above one — so
569+
* the field cannot know which element should carry it. Use the function form
570+
* whenever the child renders a focusable control, or its `hint`/`error` text
571+
* is rendered but never announced.
572+
*/
573+
children: React.ReactNode | ((aria: ChipModalFieldAria) => React.ReactNode)
555574
}
556575

557576
export type ChipModalFieldProps =
@@ -718,7 +737,7 @@ function renderChipModalControl(
718737
case 'emails':
719738
return <ChipModalEmailsControl {...props} id={id} errorId={errorId} />
720739
case 'custom':
721-
return props.children
740+
return typeof props.children === 'function' ? props.children(aria) : props.children
722741
}
723742
}
724743

0 commit comments

Comments
 (0)