feat: add Storybook stories for WidgetInputText and WidgetTextarea (#9398)

Add Storybook stories for WidgetInputText and WidgetTextarea, aligned
with the Figma Design System spec.

Task: COM-15821

## Summary

Add comprehensive Storybook stories for text widget components and
implement missing Figma design system variants for WidgetInputText.

## Changes

- **WidgetInputText component enhancements**:
- Add `size` prop (`medium` | `large`) matching Figma size variants
(32px / 40px)
- Add `invalid` prop with destructive border style per Figma Invalid
state
- Add `loading` prop showing spinning loader icon per Figma Status state
  - Add hover background (`bg-component-node-widget-background-hovered`)
  - Fix `readonly` not being applied from `widget.options.read_only`
- **WidgetTextarea component fixes**:
  - Show copy button on hover for all states (not just read-only)
  - Apply `text-component-node-foreground` token to copy icon
  - Add hover background to wrapper
- **Storybook stories**:
- WidgetInputText: Default, Disabled, Invalid, Status, WithPlaceholder,
WithLabel stories
- WidgetTextarea: Default, Disabled, HiddenLabel, WithPlaceholder
stories
  - Interactive controls for size, readOnly, disabled, invalid, loading

## Review Focus

- Figma alignment: size/invalid/loading/status variants for
WidgetInputText
- Copy icon color token (`text-component-node-foreground`) for
light/dark theme support
- `layoutWidget` computed pattern to merge `borderStyle` with invalid
state

## Screenshots (if applicable)

<!-- Add screenshots or video recording to help explain your changes -->

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dante
2026-03-06 08:42:53 +09:00
committed by GitHub
parent b2915ed42a
commit 5843dced84
5 changed files with 357 additions and 17 deletions

View File

@@ -1,13 +1,28 @@
<template>
<WidgetLayoutField :widget="widget">
<InputText
v-model="modelValue"
v-bind="filteredProps"
:class="cn(WidgetInputBaseClass, 'w-full text-xs py-2 px-4')"
:aria-label="widget.name"
size="small"
:pt="{ root: 'truncate min-w-[4ch]' }"
/>
<WidgetLayoutField :widget="layoutWidget">
<div class="relative">
<Loader
v-if="loading"
size="sm"
class="absolute left-3 top-1/2 z-10 -translate-y-1/2 text-component-node-foreground"
/>
<InputText
v-model="modelValue"
v-bind="filteredProps"
:class="
cn(
WidgetInputBaseClass,
'w-full px-4 hover:bg-component-node-widget-background-hovered',
size === 'large' ? 'text-sm py-3' : 'text-xs py-2',
loading && 'pl-9'
)
"
:aria-label="widget.name"
:readonly="isReadOnly"
size="small"
:pt="{ root: 'truncate min-w-[4ch]' }"
/>
</div>
</WidgetLayoutField>
</template>
@@ -15,6 +30,7 @@
import InputText from 'primevue/inputtext'
import { computed } from 'vue'
import Loader from '@/components/common/Loader.vue'
import type { SimplifiedWidget } from '@/types/simplifiedWidget'
import { cn } from '@/utils/tailwindUtil'
import {
@@ -25,13 +41,34 @@ import {
import { WidgetInputBaseClass } from './layout'
import WidgetLayoutField from './layout/WidgetLayoutField.vue'
const props = defineProps<{
const {
widget,
size = 'medium',
invalid = false,
loading = false
} = defineProps<{
widget: SimplifiedWidget<string>
size?: 'medium' | 'large'
invalid?: boolean
loading?: boolean
}>()
const modelValue = defineModel<string>({ default: '' })
const filteredProps = computed(() =>
filterWidgetProps(props.widget.options, INPUT_EXCLUDED_PROPS)
filterWidgetProps(widget.options, INPUT_EXCLUDED_PROPS)
)
const isReadOnly = computed(() =>
Boolean(widget.options?.read_only || widget.options?.disabled)
)
const layoutWidget = computed(() => ({
name: widget.name,
label: widget.label,
borderStyle: cn(
widget.borderStyle,
invalid && 'border border-destructive-background'
)
}))
</script>