Skip to content

Tangible UI integration#69

Closed
nicolas-jaussaud wants to merge 29 commits intomainfrom
feature/tui-integration
Closed

Tangible UI integration#69
nicolas-jaussaud wants to merge 29 commits intomainfrom
feature/tui-integration

Conversation

@nicolas-jaussaud
Copy link
Contributor

No description provided.

juliacanzani and others added 29 commits February 18, 2026 22:22
TUI now handles text addon styling natively via .is-text modifier —
surface background, border separators, and group padding adjustment
are all handled upstream. Scope legacy input styles to .tf-text-legacy.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Button: add data-test-id and aria-label props, extract ForwardedTuiProps
type, fix LegacyButton ref binding, add isDevBuild check.
Imports: convert barrel imports to direct file paths across dynamic
components to avoid circular dependencies.
Config: disable Storybook react-docgen, bump @tangible/ui to ^0.0.3,
add wp.scss control-height and Button typography tokens.
Examples: add dynamic value registration and ComboBox layout demos.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ProseMirror suite: schema, tokeniser, serialiser, node views, and
plugins (single-line, replace-on-type, dedup-ids, placeholder) for
editing text with embedded [[dynamic::value]] tokens.

DynamicEditor: React component lazy-loaded by text/textarea fields,
manages ProseMirror lifecycle with imperative insert/update API.

DynamicFieldSettings: modal for inserting/editing dynamic values with
built-in (ComboBox picker) and custom (free-text) modes.

Utilities: input masking hook (digit/letter/alphanumeric patterns with
IME and Android support), mask validation, and ID generation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Text field now routes between dynamic (ProseMirror), masked (input
mask hook), and plain (TUI TextInput) paths. Textarea gains the same
dynamic routing for multi-line mode.

Uses TUI Field compound component for consistent ARIA wiring. Adds
Text.stories.tsx covering all three code paths. Includes build output.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…d import isDev

Replace hasWarnedAboutSpanButton/hasWarnedAboutMissingLabel module-level
singletons with per-instance useRef(false) guards. Replace hand-rolled
isDevBuild const with shared isDev() utility. Add onChange dev warning
via useEffect+useRef guard.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…t, expand span fragility comment

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add typed interface covering all used props (dynamic, inputMask, prefix,
suffix, value, onChange, name, placeholder, readOnly, isDisabled,
isRequired, isInvalid, error, label, labelVisuallyHidden, description,
descriptionVisuallyHidden, className, inputClassName, size, type).
Import SizeStandard and TextInputType from @tangible/ui for accurate
size/type prop types. Fix useRef() no-arg call and cast useTextField
args (both will be properly refactored in tasks 5-6).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Move useTextField (react-aria) into an internal DynamicTextField component
so it is only called on the dynamic/ProseMirror path. Wrap TextField with
forwardRef<HTMLInputElement> and pass the forwarded ref to TuiTextInput in
the TUI path via a callback ref that also handles inputMaskRef.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…elperText comment

TUI TextInput forwards its ref directly to the <input> element, so
node.querySelector('input') is unnecessary. Also adds an explanatory
comment on the tui-visually-hidden className workaround for Field.HelperText.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add Jest tests for: Button with mapped layout (primary) renders TUI
Button; Button with unmapped layout falls back to LegacyButton; Text
without dynamic prop renders TUI TextInput (.tf-text-tui + input);
Text onChange does not fire on initial mount.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add @babel/preset-typescript with allExtensions/isTSX for .ts files with JSX
- Add custom babel-plugin-import-meta-env to shim import.meta.env for CJS
- Configure transformIgnorePatterns to transform @tangible/ui through Babel
- Add moduleNameMapper for Jest 29 package exports compatibility
- Explicit transform config so Babel applies to node_modules too
- Bump @tangible/ui from ^0.0.3 to ^0.0.4

Goes from 0/33 suites passing to 19/33 (333 tests passing).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Harness config, build prompt, specs (wrapper-contract, fix-button,
fix-text), plan, and progress log. All 8 initial tasks complete.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…pound

Replace react-aria useCheckbox/useToggleState/useField with TUI Checkbox.
Add FieldsCheckboxProps interface, forwardRef<HTMLInputElement>, Field
compound wrapper. Map isDisabled→disabled, onChange→onCheckedChange.
Preserve hidden input for PHP form submission. Sync external value
changes (repeater bulk-select) via useEffect without calling onChange.
Add render-time label warning to maintain a11y parity with react-aria.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove local checked state and value-sync useEffect. Derive boolValue
directly from the value prop — when provided, pass as controlled
checked={boolValue}; when absent, use defaultChecked={false}. Simplifies
onCheckedChange to call onChange directly without setChecked. No
mount-fire bug in TUI path (onCheckedChange is interaction-only).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace react-aria useSwitch/useToggleState/useFocusRing with TUI Switch.
Add FieldsSwitchProps interface, forwardRef<HTMLButtonElement>, hidden input.
Use useState + useEffect for controlled value sync from value prop.
Update FieldGroup test to click [role="switch"] instead of .tf-switch-label.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove local checked state and useEffect value sync. Derive boolValue
directly from value prop; use checked={boolValue} when controlled,
defaultChecked={false} otherwise. TUI onCheckedChange is
interaction-only so no mountedRef guard needed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… compound

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace react-aria useRadio + RadioContext with TUI Radio. Add
FieldsRadioProps interface, forwardRef<HTMLButtonElement>. Map
children → TUI label prop. Remove RadioContext stub from RadioGroup.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…pound

Extract DynamicTextarea sub-component (useTextField isolated to dynamic path).
Replace native textarea path with TUI Textarea + Field compound. Add
FieldsTextareaProps interface, forwardRef<HTMLTextAreaElement>. TUI native
onChange maps event.target.value to Fields onChange(value). Preserves
data-identifier via TextareaHTMLAttributes spread.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…hange on mount

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…pping

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tarea, Notice

- Augment Checkbox.test.tsx with TUI path tests (onChange-on-mount, controlled value, disabled)
- Add Switch.test.tsx: renders tf-switch + [role="switch"], onChange guards, aria-checked, disabled
- Add Radio.test.tsx: renders tf-radio-group, options, onChange guards, disabled
- Add Textarea.test.tsx: renders tf-text-area + textarea, controlled value, readOnly
- Add Notice.test.tsx: renders tf-notice, message content, type→theme mapping, dismissible toggle

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…a, Notice

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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.

2 participants