From 3cdcb72b64b12f268f36549d9885976466ac4bed Mon Sep 17 00:00:00 2001 From: Van Nguyen Date: Tue, 4 Aug 2026 23:33:54 +0700 Subject: [PATCH 1/9] ci: optimize workspace build script and release workflow verification --- .github/workflows/release.yml | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 642db89..f9b89b3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -31,6 +31,9 @@ jobs: - name: Build packages run: npm run build + - name: Typecheck & Verify Tests + run: npm run typecheck && npm test + - name: Create Release Pull Request or Publish to npm id: changesets uses: changesets/action@v1 diff --git a/package.json b/package.json index c3851a0..66e6d98 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "smoke" ], "scripts": { - "build": "npm run build --workspace=@dynamic-field-kit/core && npm run build --workspace=@dynamic-field-kit/react && npm run build --workspace=@dynamic-field-kit/vue && npm run build --workspace=@dynamic-field-kit/angular", + "build": "npm run build --workspace=@dynamic-field-kit/core && npm run build --workspaces --if-present", "build:changed": "node scripts/build-changed.js", "dev": "npm run dev --workspaces", "test": "npm run test --workspaces --if-present", From 11dff84bd1a44cc222d3ec65be98e122024f89f9 Mon Sep 17 00:00:00 2001 From: Van Nguyen Date: Tue, 4 Aug 2026 23:34:22 +0700 Subject: [PATCH 2/9] feat(core): export default HTML5 field types in FieldTypeMap --- packages/core/src/types.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index f3a6e74..d8066ec 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -1,8 +1,16 @@ /* eslint-disable @typescript-eslint/no-empty-interface */ export type FieldTypeKey = keyof FieldTypeMap & string; -// 👇 App sẽ augment interface này -export interface FieldTypeMap {} +// 👇 Core provides standard HTML5 input defaults; apps can extend via declaration merging +export interface FieldTypeMap { + text: string; + number: number; + select: string | number; + checkbox: boolean; + textarea: string; + password: string; + email: string; +} export type Properties = Record; From 6241919fe9427796ba7d355a7ae385ee067857ea Mon Sep 17 00:00:00 2001 From: Van Nguyen Date: Tue, 4 Aug 2026 23:35:57 +0700 Subject: [PATCH 3/9] feat(react): implement default HTML5 built-in fallback renderers --- .../react/src/components/DynamicInput.tsx | 5 +- packages/react/src/defaultRenderers.tsx | 199 ++++++++++++++++++ packages/react/src/index.ts | 1 + packages/react/test/defaultRenderers.test.tsx | 77 +++++++ 4 files changed, 281 insertions(+), 1 deletion(-) create mode 100644 packages/react/src/defaultRenderers.tsx create mode 100644 packages/react/test/defaultRenderers.test.tsx diff --git a/packages/react/src/components/DynamicInput.tsx b/packages/react/src/components/DynamicInput.tsx index cecbbb1..e74895f 100644 --- a/packages/react/src/components/DynamicInput.tsx +++ b/packages/react/src/components/DynamicInput.tsx @@ -4,6 +4,7 @@ import { Properties, } from '@dynamic-field-kit/core'; import React, { ReactNode, useMemo } from 'react'; +import { getDefaultRenderer } from '../defaultRenderers'; import { useFieldRegistry } from '../FieldRegistryContext'; interface Props { @@ -54,7 +55,9 @@ const DynamicInputInner = ({ // Memoize renderer lookup to avoid unnecessary work on re-renders const Renderer = useMemo( - () => registry.get(type) as React.ComponentType, + () => + ((registry.get(type) as React.ComponentType) || + getDefaultRenderer(type)) as React.ComponentType, [registry, type] ); diff --git a/packages/react/src/defaultRenderers.tsx b/packages/react/src/defaultRenderers.tsx new file mode 100644 index 0000000..3fa18f8 --- /dev/null +++ b/packages/react/src/defaultRenderers.tsx @@ -0,0 +1,199 @@ +import { FieldRendererProps } from '@dynamic-field-kit/core'; +import React from 'react'; + +export interface DefaultInputProps extends FieldRendererProps { + inputType?: string; +} + +export const DefaultTextRenderer: React.FC = ({ + value, + onValueChange, + onBlur, + disabled, + readOnly, + required, + placeholder, + id, + className, + ariaInvalid, + ariaDescribedBy, + ariaRequired, + inputType = 'text', +}) => ( + onValueChange?.(e.target.value)} + onBlur={onBlur} + disabled={disabled} + readOnly={readOnly} + required={required} + placeholder={placeholder} + aria-invalid={ariaInvalid} + aria-describedby={ariaDescribedBy} + aria-required={ariaRequired} + /> +); + +export const DefaultNumberRenderer: React.FC = ({ + value, + onValueChange, + onBlur, + disabled, + readOnly, + required, + placeholder, + id, + className, + ariaInvalid, + ariaDescribedBy, + ariaRequired, +}) => ( + + onValueChange?.( + e.target.value === '' ? undefined : Number(e.target.value) + ) + } + onBlur={onBlur} + disabled={disabled} + readOnly={readOnly} + required={required} + placeholder={placeholder} + aria-invalid={ariaInvalid} + aria-describedby={ariaDescribedBy} + aria-required={ariaRequired} + /> +); + +export const DefaultPasswordRenderer: React.FC = ( + props +) => ; + +export const DefaultEmailRenderer: React.FC = (props) => ( + +); + +export const DefaultTextareaRenderer: React.FC = ({ + value, + onValueChange, + onBlur, + disabled, + readOnly, + required, + placeholder, + id, + className, + ariaInvalid, + ariaDescribedBy, + ariaRequired, +}) => ( +