Conversation
…ith Tailwind styles
…t error state handling
* polish component states with hover, focus, and disabled styles * add useForm hook and FormContext for form state management * add Form wrapper component with submit handling * export Form, useForm, and FormContext from package * increase timeout for autoDismiss test stability * add PasswordInput component with visibility toggle and strength meter * add NumberInput component with increment buttons and formatting * add DatePicker component with calendar popup * add PhoneInput component with country code selection * feat: add FileInput component with drag-and-drop and preview * feat: add ColorPicker component with swatches and RGB/HSL inputs * feat: add Switch component with toggle states and customization * refactor: restrict public API to DynamicForm export only * refactor: restructure codebase to CHM architecture with core/, models/, fields/, and layout/ folders * chore: remove legacy code and consolidate to CHM architecture * feat(testing): add test suite achieving 80%+ coverage (192 tests) * docs: enhance JSDoc for FieldType and ConditionalOperator, add changeset * refactor: fix layer import rules and component default exports * style: add field components with improved styling * restore the checkbox field * restore the password field * feat: add PhoneField with country selector and countries.json data (240+ countries, flagcdn.com flags) * password field style adjustments * feat(FileField): add accept, maxFileSize, and multiple config options with client-side validation * adjust radio and checkbox fields styles * adjust szitch field styles * feat: add SliderField with range input, editable value sync, and min/max/step config options * feat: add RangeSliderField with dual-thumb slider and editable from/to inputs * feat: add OTPField with configurable length and Full paste support * feat: add TagsField with multi-tag input, paste support, and configurable limits * feat: add RatingField with star rating, half-star support, and keyboard navigation * feat: add TimeField with step interval * feat: add DateTimeField with combined date and time input * test: add comprehensive tests for new field components * fix: unify checkbox styles across components * feat: add MultiSelectField with search, tags, and keyboard navigation * fix: remove focus styles from rating field * feat: update SelectField to use custom dropdown with search matching MultiSelectField style * fix: update field and form tests * feat: update time, date and dateTime fields styles to have custom dropdowns and pickers * fix: adjust select and multi select fields styles * fix: update dropdowns and pickers positioning and style across components * feat: add responsive measures to form fields * feat: update OTP field styles to support mobile layout * feat(i18n): add internationalization with English and French locale support * feat(form): automatically scroll to first invalid field and focus it on submit * feat(ArrayField): refactor to reuse Field components via scoped context * feat: add translation content for Array field and optional params * refactor: remove unused config param * feat(layout): add grid-based layout system with sections and responsive columns * fix(ArrayField): enable per-field Zod validation and error display for nested fields * refactore and remove duplicate logic * address and fix the Sonar annotated findings * fix: formatting issues --------- Co-authored-by: Zaiidmo <zaiidmoumnii@gmail.com>
|
There was a problem hiding this comment.
Pull request overview
This PR evolves the repository from a template into the initial release of @ciscode/ui-form-kit, adding a full React form component system (fields, layout, wizard mode), a core utilities layer (validation, conditional logic, i18n, grid helpers), and updated build/test/CI tooling to support publishing.
Changes:
- Introduces core modules (
src/core,src/models,src/locales) and new public exports insrc/index.ts. - Adds many form UI components + hooks (field implementations, layout primitives, context/i18n, wizard navigation, async validation, field arrays).
- Updates toolchain for testing (Vitest + RTL + coverage), styling (Tailwind build + distributed CSS), linting/formatting, and CI/release workflows.
Reviewed changes
Copilot reviewed 114 out of 118 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| vitest.setup.ts | Adds RTL cleanup + jest-dom setup for Vitest. |
| vitest.config.ts | Switches to jsdom + setupFiles + coverage config/thresholds. |
| tsup.config.ts | Standardizes config formatting; exports config constant. |
| tsconfig.json | Adds global test types to main TS config. |
| tailwind.config.js | Adds Tailwind content configuration. |
| src/vitest.d.ts | Adds Vitest/jest-dom global type references under src/. |
| src/utils/noop.ts | Removes placeholder util. |
| src/utils/index.ts | Removes placeholder util index exports. |
| src/styles.css | Adds Tailwind import + library utility/responsiveness CSS. |
| src/models/ValidationRule.ts | Adds sync/async validation rule type contracts. |
| src/models/StepConfig.ts | Adds wizard step configuration contract. |
| src/models/SectionConfig.ts | Adds section/layout contracts + type guards. |
| src/models/index.ts | Adds model type re-exports. |
| src/models/FormState.ts | Defines form state/context shape contracts. |
| src/models/FieldConfig.ts | Defines field configuration contract (including advanced field options). |
| src/locales/index.ts | Exports locale dictionaries. |
| src/locales/fr.ts | Adds French translation dictionary. |
| src/locales/en.ts | Adds English translation dictionary. |
| src/index.ts | Replaces wildcard exports with curated public API surface. |
| src/hooks/useNoop.ts | Removes placeholder hook. |
| src/hooks/useI18n.ts | Adds hook to access i18n context. |
| src/hooks/useFormStep.ts | Adds wizard step navigation + per-step validation. |
| src/hooks/useFormContext.ts | Adds public wrapper hook over internal context hook. |
| src/hooks/useFieldArray.ts | Adds hook for array/repeatable field management. |
| src/hooks/useAsyncValidation.ts | Adds debounced async validation with AbortController cancellation. |
| src/hooks/index.ts | Replaces placeholder with curated hook exports. |
| src/hooks/FormKitContext.ts | Adds internal FormKit context + access hook with error message. |
| src/hooks/tests/useFormContext.test.tsx | Adds tests for context access hooks. |
| src/core/validator.ts | Adds Zod error mapping + sync/field validation helpers. |
| src/core/types.ts | Adds core form types + FieldType enum + constants. |
| src/core/schema-helpers.ts | Adds helpers to build/merge Zod schemas. |
| src/core/index.ts | Adds core barrel exports. |
| src/core/i18n.ts | Adds translation key types + nested lookup + interpolation. |
| src/core/grid.ts | Adds grid class helpers for sections/col spans. |
| src/core/errors.ts | Adds custom error class hierarchy. |
| src/core/conditional.ts | Adds conditional visibility evaluation utilities. |
| src/core/tests/validator.test.ts | Adds tests for validator utilities. |
| src/core/tests/grid.test.ts | Adds tests for grid helpers. |
| src/core/tests/conditional.test.ts | Adds tests for conditional logic. |
| src/components/NoopButton.tsx | Removes placeholder component. |
| src/components/layout/index.ts | Adds layout barrel exports. |
| src/components/layout/FormSection.tsx | Adds section rendering with per-section grid + border behavior. |
| src/components/layout/FormActions.tsx | Adds submit/reset/prev controls with loading state. |
| src/components/layout/FieldLabel.tsx | Adds standardized label/legend with required indicator. |
| src/components/layout/FieldGroup.tsx | Adds field grouping wrapper with fieldset/legend. |
| src/components/layout/FieldError.tsx | Adds standardized error message component. |
| src/components/layout/tests/FormSection.test.tsx | Adds tests for FormSection behavior/layout. |
| src/components/layout/tests/FormActions.test.tsx | Adds tests for action buttons + submitting state. |
| src/components/index.ts | Replaces placeholder exports with real module exports + provider export. |
| src/components/form/index.ts | Adds form module barrel exports. |
| src/components/form/FormStepper.tsx | Adds wizard stepper UI with aria labels and navigation. |
| src/components/form/DynamicFormStep.tsx | Adds per-step rendering wrapper for wizard mode. |
| src/components/fields/TextField.tsx | Adds text/email/password/number input field implementation. |
| src/components/fields/TextareaField.tsx | Adds textarea field implementation. |
| src/components/fields/SwitchField.tsx | Adds accessible switch/toggle field implementation. |
| src/components/fields/SliderField.tsx | Adds slider field with optional numeric input + track fill styling. |
| src/components/fields/RadioGroupField.tsx | Adds radio group field implementation. |
| src/components/fields/PasswordField.tsx | Adds password field with show/hide toggle. |
| src/components/fields/OTPField.tsx | Adds OTP input with focus management + paste support. |
| src/components/fields/index.ts | Adds fields barrel exports. |
| src/components/fields/FileField.tsx | Adds file input with type/size validation + hints. |
| src/components/fields/Field.tsx | Adds field router + conditional visibility handling. |
| src/components/fields/CheckboxField.tsx | Adds checkbox field with custom visuals + a11y. |
| src/components/fields/tests/TimeField.test.tsx | Adds interaction/a11y tests for time field. |
| src/components/fields/tests/TagsField.test.tsx | Adds interaction tests for tags field. |
| src/components/fields/tests/SliderField.test.tsx | Adds tests for slider behavior and constraints. |
| src/components/fields/tests/RatingField.test.tsx | Adds tests for rating field interaction and a11y. |
| src/components/fields/tests/RangeSliderField.test.tsx | Adds tests for range slider behavior/clamping. |
| src/components/fields/tests/PhoneField.test.tsx | Adds tests for phone field dropdown + value preservation. |
| src/components/fields/tests/PasswordField.test.tsx | Adds tests for password toggle + a11y. |
| src/components/fields/tests/OTPField.test.tsx | Adds tests for OTP input behavior. |
| src/components/fields/tests/DateTimeField.test.tsx | Adds tests for datetime picker interactions. |
| src/components/fields/tests/DateField.test.tsx | Adds tests for date picker interactions. |
| src/components/context/index.ts | Adds context barrel exports. |
| src/components/context/I18nContext.tsx | Adds i18n provider/context with deep-merge for overrides. |
| src/components/context/FormKitContext.tsx | Adds provider component wrapping hooks-layer context. |
| src/components/context/tests/I18nContext.test.tsx | Adds i18n provider/hook tests + getTranslation tests. |
| README.md | Replaces template README with package documentation + quality/release info. |
| postcss.config.cjs | Adds PostCSS plugin config (tailwindcss + autoprefixer). |
| package.json | Sets version to 0.0.1; adds Tailwind build step; adds coverage scripts; exports styles.css. |
| eslint.config.js | Expands ignore list; tweaks TS/React rules. |
| .prettierrc.json | Adds additional Prettier formatting options. |
| .github/workflows/release-check.yml | Adds master PR release-check pipeline (quality/test/build/sonar). |
| .github/workflows/publish.yml | Adds publish pipeline for npm on master push. |
| .github/workflows/pr-validation.yml | Updates PR validation workflow (node 22) and removes develop push trigger. |
| .github/workflows/ci-release-check.yml | Removes old release-check workflow. |
| .github/workflows/cd-release.yml | Removes old publish workflow. |
| .github/instructions/sonarqube_mcp.instructions.md | Adds repository instructions for Sonar MCP usage. |
| .github/instructions/bugfix.instructions.md | Adds bugfix process guidelines doc. |
| .github/dependabot.yml | Adds dependabot configuration. |
| .github/CODEOWNERS | Adds code ownership rules. |
| .changeset/first-release-v0-0-1.md | Adds changeset for v0.0.1 release. |
| const { getValues } = useFormKitContext(); | ||
|
|
||
| // Check conditional visibility | ||
| const values = getValues(); | ||
| const visible = isFieldVisible(config.showWhen, config.hideWhen, values); | ||
|
|
There was a problem hiding this comment.
getValues() is typed to return Partial<TValues> (see FormContextValue), but isFieldVisible expects a full FormValues. This is a TypeScript type mismatch and will fail tsc. Consider updating evaluateRule/isFieldVisible to accept Partial<FormValues> (or Record<string, FieldValue | undefined>) since missing keys are valid in this logic, and keep the call site type-safe.
| // Column span class | ||
| const colSpanClass = config.colSpan ? `col-span-${config.colSpan}` : ''; | ||
| const wrapperClass = `formkit-field ${colSpanClass} ${config.className ?? ''}`.trim(); |
There was a problem hiding this comment.
config.colSpan can be a responsive object (see ColSpanValue), so interpolating it directly into col-span-${config.colSpan} will produce col-span-[object Object] at runtime. Use the same helper used elsewhere (getColSpanClass) or otherwise normalize responsive values before constructing class names.
| // Get current array value | ||
| const fields = useMemo(() => { | ||
| const value = getValue(name); | ||
| return (Array.isArray(value) ? value : []) as TRow[]; | ||
| }, [getValue, name]); | ||
|
|
There was a problem hiding this comment.
fields is memoized only on [getValue, name], so it will not update when the underlying array value changes (context updates will re-render but this useMemo will keep returning the old array). This will make append/remove/... operate on stale data. Prefer computing fields directly on each render, or include a dependency that actually changes with the form state (e.g., getValues() or a value snapshot from context).
| 'node_modules/**', | ||
| 'coverage/**', | ||
| '.vitest/**', | ||
| 'postcss.config.js', |
There was a problem hiding this comment.
The ignore list includes postcss.config.js, but the repo adds postcss.config.cjs. With eslint ., the .cjs file will still be linted and may error due to CommonJS globals. Either ignore postcss.config.cjs (and/or *.cjs configs) or add an override with sourceType: 'commonjs' for that file.
| 'postcss.config.js', | |
| 'postcss.config.js', | |
| 'postcss.config.cjs', |
| "scripts": { | ||
| "clean": "rimraf dist *.tsbuildinfo", | ||
| "build": "tsup", | ||
| "clean": "rimraf dist *.tsbuildinfo && rm -rf coverage", |
There was a problem hiding this comment.
clean uses rm -rf coverage, which is not cross-platform (Windows shells will fail). Since the script already uses rimraf, consider replacing rm -rf coverage with rimraf coverage (or include coverage in the existing rimraf call).
| "clean": "rimraf dist *.tsbuildinfo && rm -rf coverage", | |
| "clean": "rimraf dist *.tsbuildinfo coverage", |
| // Store single file or array based on multiple | ||
| setValue(config.key, fileList.length === 1 ? fileList[0] : fileList); | ||
| }; |
There was a problem hiding this comment.
When config.multiple is true, selecting a single file will still store a File (because the decision is based on fileList.length). This makes the stored value type inconsistent for consumers/config-driven validation. Consider using config.multiple to decide the value shape (always File[] when multiple is true, otherwise a single File or null).
| {config.options?.map((option) => { | ||
| const optionId = `${fieldId}-${option.value}`; | ||
| const isChecked = value === option.value; | ||
|
|
||
| return ( | ||
| <div key={String(option.value)} className="flex items-center gap-2"> | ||
| <input | ||
| id={optionId} | ||
| name={config.key} | ||
| type="radio" | ||
| value={String(option.value)} | ||
| checked={isChecked} | ||
| disabled={isDisabled || option.disabled} | ||
| onChange={(e) => setValue(config.key, e.target.value)} | ||
| onBlur={() => setTouched(config.key, true)} |
There was a problem hiding this comment.
FieldOption.value supports string | number, but the radio input stores String(option.value) and then writes back e.target.value (always a string). This breaks numeric options (checked comparison value === option.value will fail and the stored type changes). Consider setting the value from option.value directly (e.g. via an onChange={() => setValue(config.key, option.value)}) or parsing based on the original option type.
| export function getGridColumnsClass(columns: ColumnCount = 1): string { | ||
| if (!isResponsiveColumns(columns)) { | ||
| return `grid-cols-${columns}`; | ||
| } | ||
|
|
||
| return buildResponsiveClasses(columns, 'grid-cols'); | ||
| } |
There was a problem hiding this comment.
getGridColumnsClass({ md: 2 }) currently returns only md:grid-cols-2 (no base grid-cols-*). With CSS grid, having display: grid but no grid-template-columns can lead to unexpected implicit columns on small screens, and SectionConfig docs state a default of 1 column. Consider passing a fallback (e.g. buildResponsiveClasses(columns, 'grid-cols', 1)) so responsive configs without default still render as single-column on mobile.



Summary
Why
Checklist
npm run lintpassesnpm run typecheckpassesnpm testpassesnpm run buildpassesnpx changeset) if this affects consumersNotes