Skip to content

Conversation

@HauklandJ
Copy link
Contributor

@HauklandJ HauklandJ commented Dec 29, 2025

Description

Config in app:

      {
        "id": "personLookup",
        "type": "PersonLookup",
        "dataModelBindings": {
          "person_lookup_ssn": "StifterPerson.Foedselsnummer",
          "person_lookup_name": "StifterPerson.Etternavn",      // ← REQUIRED
          "person_lookup_last_name": "StifterPerson.Etternavn",      // ← ADDED
          "person_lookup_first_name": "StifterPerson.Fornavn",       // ← ADDED
          "person_lookup_middle_name": "StifterPerson.Mellomnavn"    // ← ADDED
        },
        "textResourceBindings": {
          "title": "Stifter",
          "description": "Her legger du inn etternavn og fødselsnummer",
          "help": "For å kunne søke i registeret må du ha fødselsnummeret til personen"
        },
        "required": true
      },

Screenshot from getting the data element in POSTMAN:

image

Backward compatible.

Related Issue(s)

Verification/QA

  • Manual functionality testing
    • I have tested these changes manually
    • Creator of the original issue (or service owner) has been contacted for manual testing (or will be contacted when released in alpha)
    • No testing done/necessary
  • Automated tests
    • Unit test(s) have been added/updated
    • Cypress E2E test(s) have been added/updated
    • No automatic tests are needed here (no functional changes/additions)
    • I want someone to help me make some tests
  • UU/WCAG (follow these guidelines until we have our own)
    • I have tested with a screen reader/keyboard navigation/automated wcag validator
    • No testing done/necessary (no DOM/visual changes)
    • I want someone to help me perform accessibility testing
  • User documentation @ altinn-studio-docs
    • Has been added/updated
    • No functionality has been changed/added, so no documentation is needed
    • I will do that later/have created an issue
  • Support in Altinn Studio
    • Issue(s) created for support in Studio
    • This change/feature does not require any changes to Altinn Studio
  • Sprint board
    • The original issue (or this PR itself) has been added to the Team Apps project and to the current sprint board
    • I don't have permissions to do that, please help me out
  • Labels
    • I have added a kind/* and backport* label to this PR for proper release notes grouping
    • I don't have permissions to add labels, please help me out

Summary by CodeRabbit

  • New Features

    • Person lookup now captures first, middle, and last names as separate fields and assembles full display names.
  • Bug Fixes

    • Display formatting updated to show complete full names (including middle name) alongside identifier.
  • Tests

    • Added test data to validate full-name resolution and display behavior.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 29, 2025

📝 Walkthrough

Walkthrough

Refactors PersonLookup to use separate first, middle, and last name bindings (and optional middle name), updates tests and display construction to assemble full name with SSN fallback, and adds new test for full-name resolution.

Changes

Cohort / File(s) Summary
Test data
src/features/expressions/shared-tests/functions/displayValue/type-PersonLookup.json, src/features/expressions/shared-tests/functions/displayValue/type-PersonLookupFullName.json
Updated expected display value to "44829800753, Sulten Karl Løve"; replaced single person_lookup_name usage with separate person_lookup_first_name, person_lookup_middle_name, person_lookup_last_name bindings; added new full-name test file.
Component logic
src/layout/PersonLookup/PersonLookupComponent.tsx, src/layout/PersonLookup/index.tsx
Populate and clear first, middle, and last name form fields on lookup/clear; suppress validation for optional middle name; change display assembly to include SSN and prefer constructed full name from parts with fallback to single Name binding.
Configuration
src/layout/PersonLookup/config.ts
Added optional data model bindings person_lookup_first_name, person_lookup_middle_name, person_lookup_last_name to the public interface; adjusted binding handling to accommodate optional middle name.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Description check ❓ Inconclusive The pull request description covers the main changes and includes a related issue reference, but several required verification/QA checklist items are unchecked or incomplete, which may indicate missing testing or documentation updates. Please clarify the testing status: specify which manual testing or automated tests were performed, confirm if documentation updates are needed, and ensure all QA checklist items are properly addressed before merge.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and accurately summarizes the main change: adding individual name element bindings to the PersonLookup component's data model configuration.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat-17304-individual-name-components

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@HauklandJ HauklandJ moved this to 👷 In progress in Team Altinn Studio Dec 29, 2025
@HauklandJ HauklandJ added backport-ignore This PR is a new feature and should not be cherry-picked onto release branches kind/feature-request New feature or request kind/product-feature Pull requests containing new features and removed kind/feature-request New feature or request labels Dec 29, 2025
@HauklandJ HauklandJ removed this from Team Studio Dec 29, 2025
@HauklandJ
Copy link
Contributor Author

/publish

@github-actions
Copy link
Contributor

github-actions bot commented Dec 29, 2025

PR release:

  • <link rel="stylesheet" type="text/css" href="https://altinncdn.no/toolkits/altinn-app-frontend/4.24.1-pr.3416.feat-17304-individual-name-components.c7132f41/altinn-app-frontend.css">
  • <script src="https://altinncdn.no/toolkits/altinn-app-frontend/4.24.1-pr.3416.feat-17304-individual-name-components.c7132f41/altinn-app-frontend.js"></script>

⚙️ Building...
✅ Done!

@HauklandJ
Copy link
Contributor Author

/publish

@github-actions
Copy link
Contributor

github-actions bot commented Dec 29, 2025

PR release:

  • <link rel="stylesheet" type="text/css" href="https://altinncdn.no/toolkits/altinn-app-frontend/4.24.1-pr.3421.feat-17304-individual-name-components.2d9c8541/altinn-app-frontend.css">
  • <script src="https://altinncdn.no/toolkits/altinn-app-frontend/4.24.1-pr.3421.feat-17304-individual-name-components.2d9c8541/altinn-app-frontend.js"></script>

⚙️ Building...
✅ Done!

@HauklandJ HauklandJ marked this pull request as ready for review December 29, 2025 15:46
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/layout/PersonLookup/config.ts (1)

39-65: Minor: Consider reordering bindings for consistency.

The bindings are ordered as last_name, first_name, middle_name. While not incorrect, it might be more intuitive to list them in natural order: first_name, middle_name, last_name.

This is purely a stylistic suggestion and doesn't affect functionality.

Suggested reordering
+      new CG.prop(
+        'person_lookup_first_name',
+        new CG.dataModelBinding()
+          .optional()
+          .setTitle('Data model binding for the first name of a person')
+          .setDescription(
+            'Describes the location in the data model where the component should store the first name of the person to look up.',
+          ),
+      ),
+      new CG.prop(
+        'person_lookup_middle_name',
+        new CG.dataModelBinding()
+          .optional()
+          .setTitle('Data model binding for the middle name of a person')
+          .setDescription(
+            'Describes the location in the data model where the component should store the middle name of the person to look up.',
+          ),
+      ),
       new CG.prop(
         'person_lookup_last_name',
         new CG.dataModelBinding()
           .optional()
           .setTitle('Data model binding for the last name of a person')
           .setDescription(
             'Describes the location in the data model where the component should store the last name of the person to look up.',
           ),
       ),
-      new CG.prop(
-        'person_lookup_first_name',
-        new CG.dataModelBinding()
-          .optional()
-          .setTitle('Data model binding for the first name of a person')
-          .setDescription(
-            'Describes the location in the data model where the component should store the first name of the person to look up.',
-          ),
-      ),
-      new CG.prop(
-        'person_lookup_middle_name',
-        new CG.dataModelBinding()
-          .optional()
-          .setTitle('Data model binding for the middle name of a person')
-          .setDescription(
-            'Describes the location in the data model where the component should store the middle name of the person to look up.',
-          ),
-      ),
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 57d7ac0 and 2d9c854.

📒 Files selected for processing (5)
  • src/features/expressions/shared-tests/functions/displayValue/type-PersonLookup.json
  • src/features/expressions/shared-tests/functions/displayValue/type-PersonLookupFullName.json
  • src/layout/PersonLookup/PersonLookupComponent.tsx
  • src/layout/PersonLookup/config.ts
  • src/layout/PersonLookup/index.tsx
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Avoid using any type or type casting (as type) in TypeScript code; improve typing by avoiding casts and anys when refactoring
Use objects for managing query keys and functions, and queryOptions for sharing TanStack Query patterns across the system for central management

Files:

  • src/layout/PersonLookup/index.tsx
  • src/layout/PersonLookup/config.ts
  • src/layout/PersonLookup/PersonLookupComponent.tsx
{**/*.module.css,**/*.{ts,tsx}}

📄 CodeRabbit inference engine (CLAUDE.md)

Use CSS Modules for component styling and leverage Digdir Design System components when possible

Files:

  • src/layout/PersonLookup/index.tsx
  • src/layout/PersonLookup/config.ts
  • src/layout/PersonLookup/PersonLookupComponent.tsx
src/layout/*/{config,Component,index,config.generated}.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Layout components must follow standardized structure with config.ts, Component.tsx, index.tsx, and config.generated.ts files

Files:

  • src/layout/PersonLookup/index.tsx
  • src/layout/PersonLookup/config.ts
🧬 Code graph analysis (3)
src/layout/PersonLookup/index.tsx (1)
src/utils/layout/useNodeItem.ts (1)
  • useNodeFormDataWhenType (97-103)
src/layout/PersonLookup/config.ts (1)
src/codegen/CG.ts (1)
  • CG (25-57)
src/layout/PersonLookup/PersonLookupComponent.tsx (1)
src/features/validation/selectors/bindingValidationsForNode.ts (1)
  • useBindingValidationsFor (17-54)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Install
🔇 Additional comments (5)
src/layout/PersonLookup/PersonLookupComponent.tsx (2)

142-144: LGTM! Individual name fields populated correctly.

The new name bindings are populated appropriately from the API response, maintaining consistency with the existing person_lookup_name and person_lookup_ssn pattern.


155-157: LGTM! Clear action handles all name fields.

The clear logic correctly resets all individual name fields alongside the existing fields, maintaining data consistency.

src/features/expressions/shared-tests/functions/displayValue/type-PersonLookup.json (1)

11-11: LGTM! Test data correctly reflects the new individual name bindings.

The test has been properly updated to:

  • Use the three new individual name bindings instead of a single name binding
  • Provide matching data model values (FirstName, MiddleName, LastName)
  • Expect the correctly formatted display value with the full name "Sulten Karl Løve"

This validates the new feature's display behavior when using individual name components.

Also applies to: 22-24, 34-36

src/features/expressions/shared-tests/functions/displayValue/type-PersonLookupFullName.json (1)

1-35: LGTM! Excellent backward compatibility test.

This new test file validates that the legacy person_lookup_name binding continues to work as expected, ensuring backward compatibility. The test correctly expects the same display output ("44829800753, Sulten Karl Løve") whether using the combined name field or the individual name parts.

This is important for ensuring existing implementations won't break when upgrading.

src/layout/PersonLookup/config.ts (1)

33-33: The optional person_lookup_name binding is already properly handled.

The code correctly handles the optional binding: the validation logic in useEmptyFieldValidationAllBindings only triggers when the component has required=true, and the implementation in index.tsx (lines 29-40) has explicit fallback logic that uses individual name fields (person_lookup_first_name, person_lookup_last_name, person_lookup_middle_name) when person_lookup_name is absent. All component consumers properly handle the optional binding with truthy checks and optional chaining, so no validation breaks.

Comment on lines 19 to 50
useDisplayData(baseComponentId: string): string {
const formData = useNodeFormDataWhenType(baseComponentId, 'PersonLookup');
return Object.values(formData ?? {}).join(', ');
const formData = useNodeFormDataWhenType(baseComponentId, 'PersonLookup') as Record<string, string | undefined>;
const data = formData ?? {};

const parts: string[] = [];

if (data.person_lookup_ssn) {
parts.push(String(data.person_lookup_ssn));
}

// Build full name from individual parts or use the Name binding
if (data.person_lookup_name) {
parts.push(String(data.person_lookup_name));
} else {
const nameParts: string[] = [];
if (data.person_lookup_first_name) {
nameParts.push(String(data.person_lookup_first_name));
}
if (data.person_lookup_middle_name) {
nameParts.push(String(data.person_lookup_middle_name));
}
if (data.person_lookup_last_name) {
nameParts.push(String(data.person_lookup_last_name));
}

if (nameParts.length > 0) {
parts.push(nameParts.join(' '));
}
}

return parts.join(', ');
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's explore the PersonLookup directory structure
find src/layout/PersonLookup -type f -name "*.ts" -o -name "*.tsx" | head -20

Repository: Altinn/app-frontend-react

Length of output: 315


🏁 Script executed:

# Search for useNodeFormDataWhenType definition
rg "useNodeFormDataWhenType" --type ts --type tsx -A 5 -B 2 | head -50

Repository: Altinn/app-frontend-react

Length of output: 96


🏁 Script executed:

# Check the full PersonLookup index file
wc -l src/layout/PersonLookup/index.tsx

Repository: Altinn/app-frontend-react

Length of output: 105


🏁 Script executed:

# Look for type definitions related to PersonLookup form data
rg "PersonLookup.*FormData|IComponentFormData" --type ts --type tsx -B 2 -A 5

Repository: Altinn/app-frontend-react

Length of output: 96


🏁 Script executed:

# Search for imports in the PersonLookup index file to understand dependencies
head -30 src/layout/PersonLookup/index.tsx

Repository: Altinn/app-frontend-react

Length of output: 1650


🏁 Script executed:

# Read the entire PersonLookup/index.tsx file
cat -n src/layout/PersonLookup/index.tsx

Repository: Altinn/app-frontend-react

Length of output: 3860


🏁 Script executed:

# Find useNodeFormDataWhenType definition
rg "function useNodeFormDataWhenType|const useNodeFormDataWhenType" --type ts -A 10

Repository: Altinn/app-frontend-react

Length of output: 922


🏁 Script executed:

# Search for PersonLookupDef to understand the class structure
rg "export.*PersonLookupDef" --type ts -A 5

Repository: Altinn/app-frontend-react

Length of output: 559


🏁 Script executed:

# Look for PersonLookup type definitions or interfaces
rg "interface.*PersonLookup|type.*PersonLookup" --type ts -B 2 -A 5

Repository: Altinn/app-frontend-react

Length of output: 3372


🏁 Script executed:

# Find IComponentFormData definition
rg "interface IComponentFormData|type IComponentFormData" --type ts -A 10

Repository: Altinn/app-frontend-react

Length of output: 802


🏁 Script executed:

# Check the config.def.generated file for PersonLookup
head -100 src/layout/PersonLookup/config.def.generated.ts

Repository: Altinn/app-frontend-react

Length of output: 175


🏁 Script executed:

# Search for PersonLookup form data bindings definition
rg "person_lookup_ssn|person_lookup_name" --type ts -B 5 -A 2 | head -50

Repository: Altinn/app-frontend-react

Length of output: 4396


🏁 Script executed:

# Find IDataModelBindings definition for PersonLookup
rg "IDataModelBindings.*PersonLookup" --type ts -B 3 -A 5

Repository: Altinn/app-frontend-react

Length of output: 1438


🏁 Script executed:

# Find BindingToValues type definition
rg "type BindingToValues|interface BindingToValues" --type ts -A 10

Repository: Altinn/app-frontend-react

Length of output: 908


🏁 Script executed:

# Check config.ts for PersonLookup bindings structure
cat src/layout/PersonLookup/config.ts | head -150

Repository: Altinn/app-frontend-react

Length of output: 2866


Remove unnecessary type casting and redundant String() conversions.

The return type of useNodeFormDataWhenType(baseComponentId, 'PersonLookup') is already IComponentFormData<'PersonLookup'>, which resolves to a typed object with specific properties (all string | undefined). The cast to Record<string, string | undefined> bypasses TypeScript's type checking and loses property information.

Replace the cast with the proper type, and remove the String() conversions throughout the function since values are already typed as string | undefined:

const formData = useNodeFormDataWhenType(baseComponentId, 'PersonLookup') ?? {};
const data: IComponentFormData<'PersonLookup'> = formData;

if (data.person_lookup_ssn) {
  parts.push(data.person_lookup_ssn);  // No String() needed
}
🤖 Prompt for AI Agents
In src/layout/PersonLookup/index.tsx around lines 19 to 50, the code casts
useNodeFormDataWhenType(...) to Record<string, string | undefined> and wraps
values in String(), which loses typing and is redundant; replace the cast by
assigning formData = useNodeFormDataWhenType(baseComponentId, 'PersonLookup') ??
{} and declare data with the proper type IComponentFormData<'PersonLookup'>,
then remove all unnecessary String() conversions (use the string | undefined
values directly when pushing into parts); ensure any needed type import is
present.

Comment on lines +93 to +97
// Filter out middle name validations since it's optional
const bindingValidations = {
...useBindingValidationsFor<'PersonLookup'>(baseComponentId),
person_lookup_middle_name: undefined,
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Consider handling optional fields in the validation system instead of manual override.

The manual suppression of person_lookup_middle_name validations by setting it to undefined is an unconventional approach that bypasses the validation framework. This creates a maintenance burden and could mask legitimate validation issues if they arise in the future.

Consider one of these alternatives:

  • Mark the field as optional in the validation configuration itself
  • Filter optional fields in useBindingValidationsFor or create a variant hook
  • Add an optional property to the binding definition that the validation system respects
Alternative approach example

If the validation system can't be easily modified, at least centralize this logic:

const rawValidations = useBindingValidationsFor<'PersonLookup'>(baseComponentId);
const bindingValidations = useMemo(() => {
  if (!rawValidations) return undefined;
  // Filter out validations for optional bindings
  return {
    ...rawValidations,
    person_lookup_middle_name: undefined,
  };
}, [rawValidations]);

Better yet, extract this into a reusable hook that accepts optional field names as a parameter.

🤖 Prompt for AI Agents
In src/layout/PersonLookup/PersonLookupComponent.tsx around lines 93 to 97, the
code manually suppresses person_lookup_middle_name validations by assigning
undefined which bypasses the validation framework; replace this ad-hoc override
by making the field optional in the validation config or centralize the
behavior: update useBindingValidationsFor (or create a variant hook) to accept a
list of optional binding names (or respect an optional property on binding
definitions) and filter out optional-field validations there, then replace the
inline undefined assignment with a call to that hook or pass the optional list
so middle name is treated as optional consistently across components.

@sonarqubecloud
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport-ignore This PR is a new feature and should not be cherry-picked onto release branches kind/product-feature Pull requests containing new features

Projects

Status: 👷 In progress

Development

Successfully merging this pull request may close these issues.

PersonLookup: Lagre til separate navnefelt

1 participant