From 6b70d683eb270e481683f7d67319518047b45671 Mon Sep 17 00:00:00 2001
From: Ramesh Mane <101566080+rameshmane7218@users.noreply.github.com>
Date: Thu, 6 Jun 2024 19:34:20 +0530
Subject: [PATCH] fix(nc-gui): form field validation issue if column title
contains `.` (#8657)
* fix(nc-gui): form field validation issue if column title contains .
* chore(nc-gui): lint
* test(nc-gui): add `.` included column name in form validation test
* test: remove only from test
* fix(nc-gui): pr review changes
---
.../nc-gui/components/smartsheet/Form.vue | 12 ++++---
.../column/UITypesOptionsWithSearch.vue | 3 +-
.../components/smartsheet/grid/Table.vue | 2 +-
.../nc-gui/composables/useFormViewStore.ts | 29 ++++++++++++---
.../composables/useSharedFormViewStore.ts | 35 ++++++++++++++++---
.../[typeOrId]/form/[viewId]/index/index.vue | 16 ++++++---
.../[typeOrId]/form/[viewId]/index/survey.vue | 15 +++++---
packages/nc-gui/utils/formValidations.ts | 15 ++++++++
.../playwright/pages/Dashboard/Form/index.ts | 4 +--
.../tests/db/views/viewForm.spec.ts | 8 ++---
10 files changed, 107 insertions(+), 32 deletions(-)
diff --git a/packages/nc-gui/components/smartsheet/Form.vue b/packages/nc-gui/components/smartsheet/Form.vue
index d025838c99..4a634dc796 100644
--- a/packages/nc-gui/components/smartsheet/Form.vue
+++ b/packages/nc-gui/components/smartsheet/Form.vue
@@ -85,6 +85,7 @@ const {
validateInfos,
validate,
clearValidate,
+ fieldMappings,
} = useProvideFormViewStore(meta, view, formViewData, updateFormView, isEditable)
const { preFillFormSearchParams } = storeToRefs(useViewsStore())
@@ -198,9 +199,9 @@ async function submitForm() {
}
try {
- await validate([...Object.keys(formState.value)])
+ await validate(Object.keys(formState.value).map((title) => fieldMappings.value[title]))
} catch (e: any) {
- if (e.errorFields.length) {
+ if (e?.errorFields?.length) {
message.error(t('msg.error.someOfTheRequiredFieldsAreEmpty'))
return
}
@@ -586,7 +587,7 @@ watch(
updatePreFillFormSearchParams()
try {
- await validate([...Object.keys(formState.value)])
+ await validate(Object.keys(formState.value).map((title) => fieldMappings.value[title]))
} catch {}
},
{
@@ -1103,9 +1104,10 @@ useEventListener(
-import { UITypes } from 'nocodb-sdk'
-import { UITypesName } from 'nocodb-sdk'
+import { UITypes, UITypesName } from 'nocodb-sdk'
const props = defineProps<{
options: typeof uiTypes
diff --git a/packages/nc-gui/components/smartsheet/grid/Table.vue b/packages/nc-gui/components/smartsheet/grid/Table.vue
index 37f986c8bb..70bc84c7d2 100644
--- a/packages/nc-gui/components/smartsheet/grid/Table.vue
+++ b/packages/nc-gui/components/smartsheet/grid/Table.vue
@@ -2772,7 +2772,7 @@ onKeyStroke('ArrowDown', onDown)
}
.nc-grid-add-new-row {
- :deep(.ant-btn.ant-dropdown-trigger.ant-btn-icon-only){
+ :deep(.ant-btn.ant-dropdown-trigger.ant-btn-icon-only) {
@apply !flex items-center justify-center;
}
}
diff --git a/packages/nc-gui/composables/useFormViewStore.ts b/packages/nc-gui/composables/useFormViewStore.ts
index 26b1c03b8b..a9d66ca981 100644
--- a/packages/nc-gui/composables/useFormViewStore.ts
+++ b/packages/nc-gui/composables/useFormViewStore.ts
@@ -40,10 +40,19 @@ const [useProvideFormViewStore, useFormViewStore] = useInjectionState(
return null
})
+ const fieldMappings = computed(() => {
+ const uniqueFieldNames: Set = new Set()
+
+ return visibleColumns.value.reduce((acc, c) => {
+ acc[c.title] = getValidFieldName(c.title, uniqueFieldNames)
+ return acc
+ }, {} as Record)
+ })
+
const validators = computed(() => {
const rulesObj: Record = {}
- if (!visibleColumns.value) return rulesObj
+ if (!visibleColumns.value || !Object.keys(fieldMappings.value).length) return rulesObj
for (const column of visibleColumns.value) {
let rules: RuleObject[] = [
@@ -73,19 +82,30 @@ const [useProvideFormViewStore, useFormViewStore] = useInjectionState(
rules = [...rules, ...additionalRules]
if (rules.length) {
- rulesObj[column.title!] = rules
+ rulesObj[fieldMappings.value[column.title!]] = rules
}
}
return rulesObj
})
+ const fieldMappingFormState = computed(() => {
+ if (!Object.keys(fieldMappings.value).length) return {}
+
+ return Object.keys(formState.value).reduce((acc, key) => {
+ acc[fieldMappings.value[key]] = formState.value[key]
+ return acc
+ }, {} as Record)
+ })
+
// Form field validation
- const { validate, validateInfos, clearValidate } = useForm(formState, validators)
+ const { validate, validateInfos, clearValidate } = useForm(fieldMappingFormState, validators)
const validateActiveField = async (col: ColumnType) => {
+ if (!col.title) return
+
try {
- await validate(col.title)
+ await validate(fieldMappings.value[col.title])
} catch {}
}
@@ -134,6 +154,7 @@ const [useProvideFormViewStore, useFormViewStore] = useInjectionState(
validate,
validateInfos,
clearValidate,
+ fieldMappings,
}
},
'form-view-store',
diff --git a/packages/nc-gui/composables/useSharedFormViewStore.ts b/packages/nc-gui/composables/useSharedFormViewStore.ts
index f39b4a04e6..e68be73575 100644
--- a/packages/nc-gui/composables/useSharedFormViewStore.ts
+++ b/packages/nc-gui/composables/useSharedFormViewStore.ts
@@ -187,10 +187,19 @@ const [useProvideSharedFormStore, useSharedFormStore] = useInjectionState((share
}
}
+ const fieldMappings = computed(() => {
+ const uniqueFieldNames: Set = new Set()
+
+ return formColumns.value.reduce((acc, c) => {
+ acc[c.title!] = getValidFieldName(c.title!, uniqueFieldNames)
+ return acc
+ }, {} as Record)
+ })
+
const validators = computed(() => {
const rulesObj: Record = {}
- if (!formColumns.value) return rulesObj
+ if (!formColumns.value || !Object.keys(fieldMappings.value).length) return rulesObj
for (const column of formColumns.value) {
let rules: RuleObject[] = [
@@ -220,7 +229,7 @@ const [useProvideSharedFormStore, useSharedFormStore] = useInjectionState((share
rules = [...rules, ...additionalRules]
if (rules.length) {
- rulesObj[column.title!] = rules
+ rulesObj[fieldMappings.value[column.title!]] = rules
}
}
@@ -228,7 +237,19 @@ const [useProvideSharedFormStore, useSharedFormStore] = useInjectionState((share
})
const validationFieldState = computed(() => {
- return { ...formState.value, ...additionalState.value }
+ if (!Object.keys(fieldMappings.value).length) return {}
+
+ const fieldMappingFormState = Object.keys(formState.value).reduce((acc, key) => {
+ acc[fieldMappings.value[key]] = formState.value[key]
+ return acc
+ }, {} as Record)
+
+ const fieldMappingAdditionalState = Object.keys(additionalState.value).reduce((acc, key) => {
+ acc[fieldMappings.value[key]] = additionalState.value[key]
+ return acc
+ }, {} as Record)
+
+ return { ...fieldMappingFormState, ...fieldMappingAdditionalState }
})
const { validate, validateInfos, clearValidate } = useForm(validationFieldState, validators)
@@ -254,7 +275,10 @@ const [useProvideSharedFormStore, useSharedFormStore] = useInjectionState((share
handleAddMissingRequiredFieldDefaultState()
try {
- await validate([...Object.keys(formState.value), ...Object.keys(additionalState.value)])
+ await validate([
+ ...Object.keys(formState.value).map((title) => fieldMappings.value[title]),
+ ...Object.keys(additionalState.value).map((title) => fieldMappings.value[title]),
+ ])
return true
} catch (e: any) {
if (e.errorFields.length) {
@@ -571,7 +595,7 @@ const [useProvideSharedFormStore, useSharedFormStore] = useInjectionState((share
additionalState,
async () => {
try {
- await validate(Object.keys(additionalState.value))
+ await validate(Object.keys(additionalState.value).map((title) => fieldMappings.value[title]))
} catch {}
},
{
@@ -606,6 +630,7 @@ const [useProvideSharedFormStore, useSharedFormStore] = useInjectionState((share
additionalState,
isRequired,
handleAddMissingRequiredFieldDefaultState,
+ fieldMappings,
}
}, 'shared-form-view-store')
diff --git a/packages/nc-gui/pages/index/[typeOrId]/form/[viewId]/index/index.vue b/packages/nc-gui/pages/index/[typeOrId]/form/[viewId]/index/index.vue
index e5695aab78..f93f44aef3 100644
--- a/packages/nc-gui/pages/index/[typeOrId]/form/[viewId]/index/index.vue
+++ b/packages/nc-gui/pages/index/[typeOrId]/form/[viewId]/index/index.vue
@@ -17,6 +17,7 @@ const {
progress,
validateInfos,
validate,
+ fieldMappings,
} = useSharedFormStoreOrThrow()
const { isMobileMode } = storeToRefs(useConfigStore())
@@ -175,7 +176,12 @@ const onDecode = async (scannedCodeValue: string) => {