feat(morfo): ariaBoolean entry flag — full de-dup of NumberField Input aria

`MorfoAriaEntry.ariaBoolean?: boolean` makes a propRef-valued ARIA attr compile
to aria-boolean mode (renders the "true"/"false" string), the same shape a
stateRef already gets. Without it, `aria-required` ← `required` prop compiled to
raw mode → renderProps() emitted a boolean (DOM-identical, but a different props
shape, and it forced the provider to keep stringifying).

- types/schema/compile wired for the flag.
- NumberField morfo: aria-required/disabled/readonly/invalid gain ariaBoolean.
- NumberField Input provider: dropped the 4 boolToStr overrides — role + every
  aria + data-spin-field-input now come from renderProps(); soma keeps only the
  formatted aria-valuetext, aria-describedby, native form attrs and handlers.

The Input is now fully morfo-sourced (zero re-declared attrs). Verified in
browser (aria-required="false" string, role/valuenow from morfo) + provider
tests 69/69. This is the reusable mechanism for de-duping aria booleans across
the rollout.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
active-uix
dev 4 months ago
parent 59a851850c
commit 019413501b

@ -522,7 +522,8 @@ function compileDataPlan(data: MorfoData, componentKebab: string): AttrPlan {
function compileAriaPlan(aria: MorfoAriaEntry, componentKebab: string): AttrPlan {
const condition: ConditionPlan = aria.condition ?? 'always'
const source = normalizeSource(aria.value, componentKebab)
const mode: AttrMode = aria.value.kind === 'stateRef' ? 'aria-boolean' : 'string'
const mode: AttrMode =
aria.value.kind === 'stateRef' || aria.ariaBoolean ? 'aria-boolean' : 'string'
return Object.freeze({
attr: aria.attr,
source,

@ -92,10 +92,30 @@ export const numberFieldMorfo = {
condition: { when: 'prop-defined', prop: 'max' }
},
{ attr: 'aria-valuetext', value: v.propRef('value'), severity: 'recommended' },
{ attr: 'aria-required', value: v.propRef('required'), severity: 'optional' },
{ attr: 'aria-disabled', value: v.propRef('disabled'), severity: 'optional' },
{ attr: 'aria-readonly', value: v.propRef('readonly'), severity: 'optional' },
{ attr: 'aria-invalid', value: v.propRef('invalid'), severity: 'optional' }
{
attr: 'aria-required',
value: v.propRef('required'),
severity: 'optional',
ariaBoolean: true
},
{
attr: 'aria-disabled',
value: v.propRef('disabled'),
severity: 'optional',
ariaBoolean: true
},
{
attr: 'aria-readonly',
value: v.propRef('readonly'),
severity: 'optional',
ariaBoolean: true
},
{
attr: 'aria-invalid',
value: v.propRef('invalid'),
severity: 'optional',
ariaBoolean: true
}
],
keyboard: [
{ key: 'ArrowUp', action: 'increment' },

@ -165,7 +165,8 @@ const ariaEntrySchema = object({
attr: string(),
value: valueSourceSchema,
condition: optional(conditionSchema),
severity: optional(severitySchema)
severity: optional(severitySchema),
ariaBoolean: optional(boolean())
});
const keyboardSchema = object({

@ -264,6 +264,13 @@ export interface MorfoAriaEntry {
condition?: MorfoCondition;
/** Validator severity. Defaults to `'required'`. */
severity?: MorfoSeverity;
/**
* Render the value as an ARIA boolean (`"true"`/`"false"` string) instead of
* passing it through raw. A `stateRef` value already compiles to aria-boolean;
* set this for a `propRef` boolean (e.g. `aria-required` ← `required` prop) so
* `renderProps()` stringifies it and the provider need not re-declare it.
*/
ariaBoolean?: boolean;
}
// ── Keyboard ──────────────────────────────────────────────────────────────

@ -617,15 +617,6 @@ export class NumberFieldInputProvider {
this.provider.opts.value.current !== undefined
? this.provider.formatValue(this.provider.opts.value.current)
: undefined,
// ARIA booleans: the morfo declares them (propRef), but a propRef-valued
// aria attr compiles to raw mode → renderProps emits the boolean. ARIA
// wants the "true"/"false" string, so soma stringifies (same role as the
// formatted aria-valuetext above). A future `v.ariaBool()` morfo helper
// would let these resolve from the morfo too.
'aria-required': boolToStr(this.provider.isRequired),
'aria-disabled': boolToStr(this.provider.isDisabled),
'aria-readonly': boolToStr(this.provider.isReadonly),
'aria-invalid': boolToStr(this.provider.isInvalid),
'aria-describedby': this.describedBy,
disabled: this.provider.isDisabled || undefined,
readonly: this.provider.isReadonly || undefined,

Loading…
Cancel
Save

Powered by TurnKey Linux.