Skip to content

Integrate basic fields and buttons#70

Open
juliacanzani wants to merge 80 commits into
mainfrom
feature/tui-integration
Open

Integrate basic fields and buttons#70
juliacanzani wants to merge 80 commits into
mainfrom
feature/tui-integration

Conversation

@juliacanzani

@juliacanzani juliacanzani commented Mar 24, 2026

Copy link
Copy Markdown
Contributor

Adds upgraded TUI Components for:

  • Text inputs
  • Textarea inputs
  • Buttons
  • Switch
  • Radio
  • Checkbox

⚠️ Breaking change

Text fields with prefix/suffix now save only the user's input — the affixes are visual elements and are no longer baked into the stored value.

  • Why: affixes are presentation. Composing them into stored data meant a display-copy change became a data change, and every consumer had to strip decoration before using the value. Value composition belongs to form handling, not the UI layer.
  • Impact: consumers that relied on the composed value must compose it themselves from the saved value.
  • Mitigation: previously-stored composed values are tolerated on read — affixes are stripped before input-mask validation (stripAffixes() in Text.tsx).

Changes since April (June 2026 finishing pass)

  • Merged main (conflicts confined to generated build artifacts + lockfile, resolved by rebuilding; the new UncontrolledFields tests updated to target the TUI switch markup)
  • @tangible/ui pinned to 0.0.14
  • WP theme synced with the LMS admin theme; adopts WP 7.0 admin CSS variables (--wp-admin-theme-color-darker-*, --wp-editor-canvas-background, --wp-admin-border-width-focus) with static fallbacks
  • Visual QA fixes from the legacy-styling pass:
    • Button: action-button hover states restored (a background !important override was blocking TUI's variable-driven hover)
    • Notice: stripe/inline forwarded and defaulted to true; core WP notice palette expressed via TUI semantic feedback tokens
    • Checkbox: checkmark icon restored in context bundles (legacy background shorthand was wiping the TUI SVG)
  • Breaking change + migration documented in docs/changelog.md

Status

  • Full Jest suite green (425 passed, 1 skipped)
  • Ready for review

juliacanzani and others added 30 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>
Add @floating-ui/react and @tanstack/react-table required by TUI 0.0.8.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@nicolas-jaussaud

Copy link
Copy Markdown
Contributor

Hey @juliacanzani!

The new components look great!

I did notice some styling issues with legacy controls that relies on them (mostly the button and text component)

I attempted to fix some, but it's probably not perfect. I added the legacy fields that use the button and text components in storybook, in case you want to have a look and improve it further (see associated storybook build)

I also added a section with the dynamic values in storybook, as I noticed there are some minor styling issues there

I noticed only one breaking change, in the text field value when used with a prefix or a suffix. Before, the saved value was including the prefix and the suffix, while now it only include the user input

So before merging, I believe we should:

  • Double check styling for legacy components that relies on the new button and text
  • Make sure text with prefix/suffix save value like before (unless we decide that it should be the new expected behavior)

Feel really free to push on this branch, I won't push anything until you tell me it's ok!

juliacanzani and others added 7 commits June 12, 2026 15:29
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Conflicts were confined to generated assets and the lockfile, resolved by
rebuilding (npm run build) and regenerating package-lock.json.

Also updated the new UncontrolledFields tests from main to click the
migrated TUI switch (button[role=switch]) instead of the legacy
.tf-switch-label markup.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… migration

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The secondary/outline background override pinned the background property
with !important, which blocked TUI's variable-driven hover/active states.
Set the --tui-button-bg rest-state token instead.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…semantic tokens

Forward stripe/inline props to TUI Notice (previously swallowed) with both
defaulting to true, matching wp-admin notice anatomy.

In the WP theme, the core admin notice palette (WP 7.0 tint + stripe pairs)
is expressed through the TUI semantic feedback scales (success/warning/danger
base + subtlest) so chips, buttons and notices all align. Info notices keep
core's plain-surface background via a notice-level override.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The legacy .tf-checkbox checked rule uses the background shorthand, which
resets background-image and wiped the TUI checkbox's SVG checkmark inside
context bundles (WP context showed no icon at all, since the WP theme also
hides the core ::before dashicon). Scope the legacy rule away from TUI
inputs with :not(.tui-inline-choice__control), same pattern as Notice.

Includes rebuilt assets.

Co-Authored-By: Claude Fable 5 <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