@@ -184,6 +184,22 @@ export const addFieldStatePromise = async (args: AddFieldStatePromiseArgs): Prom
184184 fieldState . fieldSchema = field
185185 }
186186
187+ // Short-circuit to prevent hidden fields from recursing and rendering.
188+ // Note: `tab` is excluded bc tab visibility is keyed by `field.id` rather than `path`.
189+ // The tab branch below owns that write and the skip-recursion.
190+ if ( passesCondition === false && field . type !== 'tab' ) {
191+ if ( fieldAffectsData ( field ) && data ?. [ field . name ] !== undefined ) {
192+ fieldState . value = data [ field . name ]
193+ fieldState . initialValue = data [ field . name ]
194+ }
195+
196+ if ( ! filter || filter ( args ) ) {
197+ state [ path ] = fieldState
198+ }
199+
200+ return
201+ }
202+
187203 if ( fieldAffectsData ( field ) && ! fieldIsHiddenOrDisabled ( field ) && field . type !== 'tab' ) {
188204 fieldPermissions =
189205 parentPermissions === true
@@ -808,10 +824,6 @@ export const addFieldStatePromise = async (args: AddFieldStatePromiseArgs): Prom
808824 state [ path ] = {
809825 disableFormData : true ,
810826 }
811-
812- if ( passesCondition === false ) {
813- state [ path ] . passesCondition = false
814- }
815827 }
816828
817829 await iterateFields ( {
@@ -851,18 +863,10 @@ export const addFieldStatePromise = async (args: AddFieldStatePromiseArgs): Prom
851863 } )
852864 } else if ( field . type === 'tab' ) {
853865 const isNamedTab = tabHasName ( field )
854- let tabSelect : SelectType | undefined
855-
856- const tabField : TabAsField = {
857- ...field ,
858- type : 'tab' ,
859- }
860-
861- let childPermissions : SanitizedFieldsPermissions = undefined
862866
863867 if ( isNamedTab ) {
864868 const shouldContinue = stripUnselectedFields ( {
865- field : tabField ,
869+ field : { ... field , type : 'tab' } ,
866870 select,
867871 selectMode,
868872 siblingDoc : data ?. [ field . name ] || { } ,
@@ -871,16 +875,34 @@ export const addFieldStatePromise = async (args: AddFieldStatePromiseArgs): Prom
871875 if ( ! shouldContinue ) {
872876 return
873877 }
878+ }
879+
880+ // Tab visibility on the client is keyed by `field.id`, not `path` (like all other fields).
881+ if ( field ?. id ) {
882+ state [ field . id ] = {
883+ passesCondition,
884+ }
885+
886+ // Flag newly added tab entries so the client accepts them during merge.
887+ // Otherwise, tabs revealed after a hidden ancestor becomes visible would never make it into client form state.
888+ if ( ! renderAllFields && ! previousFormState ?. [ field . id ] ) {
889+ state [ field . id ] . addedByServer = true
890+ }
891+ }
874892
893+ if ( ! passesCondition ) {
894+ return
895+ }
896+
897+ let childPermissions : SanitizedFieldsPermissions
898+ let tabSelect : SelectType | undefined
899+
900+ if ( isNamedTab ) {
875901 if ( parentPermissions === true ) {
876902 childPermissions = true
877903 } else {
878904 const tabPermissions = parentPermissions ?. [ field . name ]
879- if ( tabPermissions === true ) {
880- childPermissions = true
881- } else {
882- childPermissions = tabPermissions ?. fields
883- }
905+ childPermissions = tabPermissions === true ? true : tabPermissions ?. fields
884906 }
885907
886908 if ( typeof select ?. [ field . name ] === 'object' ) {
@@ -891,27 +913,6 @@ export const addFieldStatePromise = async (args: AddFieldStatePromiseArgs): Prom
891913 tabSelect = select
892914 }
893915
894- const pathSegments = path ? path . split ( '.' ) : [ ]
895-
896- // If passesCondition is false then this should always result to false
897- // If the tab has no admin.condition provided then fallback to passesCondition and let that decide the result
898- let tabPassesCondition = passesCondition
899-
900- if ( passesCondition && typeof field . admin ?. condition === 'function' ) {
901- tabPassesCondition = field . admin . condition ( fullData , data , {
902- blockData,
903- operation,
904- path : pathSegments ,
905- user : req . user ,
906- } )
907- }
908-
909- if ( field ?. id ) {
910- state [ field . id ] = {
911- passesCondition : tabPassesCondition ,
912- }
913- }
914-
915916 return iterateFields ( {
916917 id,
917918 addErrorPathToParent : addErrorPathToParentArg ,
@@ -930,7 +931,7 @@ export const addFieldStatePromise = async (args: AddFieldStatePromiseArgs): Prom
930931 omitParents,
931932 operation,
932933 parentIndexPath : indexPath ,
933- parentPassesCondition : tabPassesCondition ,
934+ parentPassesCondition : passesCondition ,
934935 parentPath : path ,
935936 parentSchemaPath : schemaPath ,
936937 permissions : childPermissions ,
@@ -951,10 +952,6 @@ export const addFieldStatePromise = async (args: AddFieldStatePromiseArgs): Prom
951952 state [ path ] = {
952953 disableFormData : true ,
953954 }
954-
955- if ( passesCondition === false ) {
956- state [ path ] . passesCondition = false
957- }
958955 }
959956
960957 return iterateFields ( {
0 commit comments