Skip to content

Conversation

@JamalAlabdullah
Copy link
Contributor

@JamalAlabdullah JamalAlabdullah commented Jan 31, 2026

Description

CLOSES:#17569

Redesignet Nøkkelord and the link to be inside addButton pluss , and removed the Alternativ ID field.

  • Added new reusable component called StudioInlineEdit
  • Added test for it
  • Added css also.
Screen.Recording.2026-02-02.at.13.30.26.mov

Verification

  • Related issues are connected (if applicable)
  • Your code builds clean without any errors or warnings
  • Manual testing done (required)
  • Relevant automated test added (if you find this hard, leave it and we'll help out)

Summary by CodeRabbit

  • New Features

    • Inline-editable text fields added to the About tab for homepage and keywords with edit/save/cancel flow.
    • New reusable inline text field component exposed in the component library.
  • Tests

    • Test suites and helpers updated for inline-edit interactions; old synchronous textbox helper removed and replaced with async inline-edit helpers.
  • Documentation

    • Localised labels and descriptions for About tab fields updated.

@JamalAlabdullah JamalAlabdullah linked an issue Jan 31, 2026 that may be closed by this pull request
@github-actions github-actions bot added skip-releasenotes Issues that do not make sense to list in our release notes frontend solution/studio/designer labels Jan 31, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 31, 2026

📝 Walkthrough

Walkthrough

Adds a new StudioInlineTextField component with view/edit inline behaviour, integrates it into AppConfigForm for homepage and keywords, updates tests and public exports, adds CSS, and adjusts Norwegian localisation keys and text.

Changes

Cohort / File(s) Summary
New inline editor component
src/Designer/frontend/libs/studio-components/src/components/StudioInlineTextField/StudioInlineTextField.tsx, src/Designer/frontend/libs/studio-components/src/components/StudioInlineTextField/StudioInlineTextField.module.css, src/Designer/frontend/libs/studio-components/src/components/StudioInlineTextField/index.ts, src/Designer/frontend/libs/studio-components/src/components/StudioInlineTextField/StudioInlineTextField.test.tsx
Adds StudioInlineTextField with view/edit modes, internal state, save/cancel actions, editing CSS, tests, and re-exports (including props type).
Public exports
src/Designer/frontend/libs/studio-components/src/components/index.ts
Exports StudioInlineTextField from the components index to include it in the public API.
AppConfigForm integration
src/Designer/frontend/app-development/features/appSettings/components/TabsContent/Tabs/AboutTab/AppConfigForm/AppConfigForm.tsx, .../AppConfigForm.module.css
Replaces StudioTextfield with StudioInlineTextField for homepage and keywords fields; updates handlers to accept string values; adds .inlineTextFieldWithIcon layout class and ensures reset/sync behaviour for keywords.
Tests & test helpers
src/Designer/frontend/app-development/features/appSettings/components/TabsContent/Tabs/AboutTab/AppConfigForm/AppConfigForm.test.tsx
Removes getOptionalTextbox; introduces getOptionalInlineEditTextbox(user, label): Promise<HTMLInputElement> and getInlineEditSaveButton() helpers; updates tests to open inline editors, type, save inline edits, then submit form and assert transitions.
Localisation (Norwegian)
src/Designer/frontend/language/src/nb.json
Removes two obsolete keys (app_settings.about_tab_alt_id_description, app_settings.about_tab_alt_id_label) and updates label/description texts for homepage and keywords fields.

Sequence Diagram(s)

sequenceDiagram
  participant User as User (tester)
  participant App as AppConfigForm
  participant Inline as StudioInlineTextField
  participant TestH as Test Helper

  User->>App: open About tab
  App->>Inline: render view mode (label + value)
  User->>Inline: click label/button (enter edit)
  Inline->>Inline: set isEditMode=true, populate inputValue
  TestH->>Inline: find inline textbox (getOptionalInlineEditTextbox)
  TestH->>Inline: type new value
  TestH->>Inline: click save (getInlineEditSaveButton)
  Inline->>App: onChange(newValue)
  Inline->>Inline: set isEditMode=false
  App->>App: update local appConfig state
  User->>App: click form save
  App->>App: persist updated config (form submit)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 I click the label, nibble a line,
I type, I save — the value's mine.
From view to edit, a gentle hop,
Keywords, homepage — all tick and bop.
The rabbit grins, then stops to bop.

🚥 Pre-merge checks | ✅ 1 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Title check ⚠️ Warning The title mentions implementing an 'addbutton' for 'optional fields', but the actual changes implement a new reusable 'StudioInlineTextField' component replacing text inputs with inline edit functionality. Update the title to accurately reflect the main change, such as: 'feat: implement StudioInlineTextField component for inline editing' or 'feat: redesign optional fields with inline edit pattern'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The description covers the key changes and includes issue reference and verification checklist, but lacks technical detail about the implementation and mentions 'StudioInlineEdit' while the actual component is named 'StudioInlineTextField'.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 17569-replace-valgfrie-felter-with-the-addnew-button-pattern

Important

Action Needed: IP Allowlist Update

If your organization protects your Git platform with IP whitelisting, please add the new CodeRabbit IP address to your allowlist:

  • 136.113.208.247/32 (new)
  • 34.170.211.100/32
  • 35.222.179.152/32

Reviews will stop working after February 8, 2026 if the new IP is not added to your allowlist.


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.

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

🤖 Fix all issues with AI agents
In `@src/Designer/frontend/language/src/nb.json`:
- Around line 302-305: The value for the localization key
"app_settings.about_tab_homepage_field_description" contains a stray "n"; update
that JSON string to read "Lim inn lenke til siden der sluttbrukere finner appen
og informasjon om den." (i.e., remove the extra "n" after "lenke" and ensure
proper spacing/punctuation), leaving the other keys
("app_settings.about_tab_homepage_field_label",
"app_settings.about_tab_keywords_description",
"app_settings.about_tab_keywords_label") unchanged.

In
`@src/Designer/frontend/libs/studio-components/src/components/StudioInlineEdit/StudioInlineEdit.tsx`:
- Around line 11-20: The icon-only action buttons in StudioInlineEdit lack
accessible names; update StudioInlineEditProps to accept caller-provided
aria-label strings (e.g., saveAriaLabel and cancelAriaLabel, and editAriaLabel
if there is an edit action) and use those props when rendering the icon buttons
inside the StudioInlineEdit component (replace the current icon-only
Button/IconButton usages around the save/cancel render paths referenced in the
component). Ensure the component falls back to sensible localised defaults when
props are not provided and apply the aria-label attributes to the exact button
elements that currently render only icons (the save and cancel action buttons
around lines ~83-88 and the edit/action buttons at ~22-31).
🧹 Nitpick comments (1)
src/Designer/frontend/libs/studio-components/src/components/StudioInlineEdit/StudioInlineEdit.test.tsx (1)

40-46: Use role-based assertion for description text.

The tests are otherwise great, but this one uses getByText. A role-based assertion keeps it aligned with the testing guideline and the accessible API.

♻️ Suggested adjustment
-    expect(screen.getByText(description)).toBeInTheDocument();
+    expect(screen.getByRole('textbox', { name: label })).toHaveAccessibleDescription(description);

@codecov
Copy link

codecov bot commented Jan 31, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.93%. Comparing base (87a2113) to head (aa46c99).
⚠️ Report is 20 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #17608      +/-   ##
==========================================
- Coverage   95.98%   95.93%   -0.06%     
==========================================
  Files        2433     2456      +23     
  Lines       30850    31254     +404     
  Branches     3598     3644      +46     
==========================================
+ Hits        29612    29983     +371     
- Misses        923      958      +35     
+ Partials      315      313       -2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/Designer/frontend/app-development/features/appSettings/components/TabsContent/Tabs/AboutTab/AppConfigForm/AppConfigForm.test.tsx (1)

22-27: ⚠️ Potential issue | 🟡 Minor

Open inline-edit mode before saving (or remove the save click).

StudioInlineEdit only renders the textbox/save button after entering edit mode. These tests either fetch a textbox directly or click the inline-edit save button after editing a regular field, so they’ll fail and/or won’t commit changes. Use getOptionalInlineEditTextbox for the inline-edit fields and drop the save click for regular textfields.

Proposed fix (apply the same pattern to the other affected tests)
-    const anInputField = getOptionalTextbox(
-      textMock('app_settings.about_tab_homepage_field_label'),
-    );
-    const newValue: string = 'A';
-    await user.type(anInputField, newValue);
-    await user.click(getInlineEditSaveButton());
+    const homepage = await getOptionalInlineEditTextbox(
+      user,
+      textMock('app_settings.about_tab_homepage_field_label'),
+    );
+    const newValue: string = 'A';
+    await user.type(homepage, newValue);
+    await user.click(getInlineEditSaveButton());
-    await user.click(getInlineEditSaveButton());

Also applies to: 232-236, 252-256, 271-275, 288-292

🤖 Fix all issues with AI agents
In
`@src/Designer/frontend/app-development/features/appSettings/components/TabsContent/Tabs/AboutTab/AppConfigForm/AppConfigForm.tsx`:
- Around line 61-66: The inline-edit for Alt ID is wired to updatedAppConfig.id
but the handler onChangeServiceId updates serviceId, so edits never appear; fix
by making the binding and updater target the same property: either change
onChangeServiceId to update id (setUpdatedAppConfig(old => ({ ...old, id:
newValue }))) or change the input's value to use updatedAppConfig.serviceId and
ensure ApplicationMetadata includes serviceId; apply the same correction to the
second occurrence referenced around the other handler (lines ~143-151) so both
read/write the same property.
🧹 Nitpick comments (1)
src/Designer/frontend/app-development/features/appSettings/components/TabsContent/Tabs/AboutTab/AppConfigForm/AppConfigForm.tsx (1)

201-238: Consider replacing the large commented JSX block with a feature flag or removal.

Leaving sizeable commented UI tends to rot; a gated render or a clean removal would be easier to maintain while #17439 is in flight.

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: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/Designer/frontend/app-development/features/appSettings/components/TabsContent/Tabs/AboutTab/AppConfigForm/AppConfigForm.test.tsx (1)

18-27: ⚠️ Potential issue | 🟠 Major

Inline‑edit tests still query a textbox without entering edit mode.

StudioInlineEdit only renders the textbox after clicking the view button, so getOptionalTextbox(...) will fail here. Use getOptionalInlineEditTextbox(...) instead.

✅ Example fix
-    const anInputField = getOptionalTextbox(
-      textMock('app_settings.about_tab_homepage_field_label'),
-    );
+    const anInputField = await getOptionalInlineEditTextbox(
+      user,
+      textMock('app_settings.about_tab_homepage_field_label'),
+    );
-    const altId = getOptionalTextbox(textMock('app_settings.about_tab_homepage_field_label'));
+    const altId = await getOptionalInlineEditTextbox(
+      user,
+      textMock('app_settings.about_tab_homepage_field_label'),
+    );
-    const altId = getOptionalTextbox(textMock('app_settings.about_tab_homepage_field_label'));
+    const altId = await getOptionalInlineEditTextbox(
+      user,
+      textMock('app_settings.about_tab_homepage_field_label'),
+    );

Also applies to: 228-235, 244-255

@JamalAlabdullah JamalAlabdullah added the squad/utforming Issues that belongs to the named squad. label Feb 3, 2026
@JamalAlabdullah JamalAlabdullah moved this to 🔎 In review in Team Altinn Studio Feb 3, 2026
Copy link
Contributor

@lassopicasso lassopicasso left a comment

Choose a reason for hiding this comment

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

Well done! In this first round of review, I’ve focused on the new Studio component StudioInlineEdit. I’ll do a second review later for the places where the component is used.

The naming StudioInlineEdit is a bit vague to me. It sounds like it could support different input types or render arbitrary children. One idea could be to make it support multiple input types via a prop (as children), but then controlled form handling (handleSave, etc.) would need to be done outside the component. We can consider consolidating that later if/when we need to handle different input types with the same UI.

For now, the component is tightly connected to StudioTextfield and only supports editing a string value. I think giving it a more descriptive name would help. Something like StudioInlineTextField seems clearer for this component. What do you think?

I see there is missing a stories file for this new studio component. That is required for studio components that is custom, as far as I know, to be able to test it in storybook.

icon?: React.ReactNode;
saveAriaLabel?: string;
cancelAriaLabel?: string;
};
Copy link
Contributor

Choose a reason for hiding this comment

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

I think the following props shouldn't be optional:

  • label: this is usually always required in a form element.
  • onChange: the core of this component is to pass changes up, and would crash if onChange is undefined.

The type of props:

  • description: to simplify/restrict what we can pass to this prop in this component, could we keep it as a string? I am unsure here, so I'm open for discussion 🙂 Perhaps it is a good idea to keep the type as ReactNode, but I don't see the immediately need for this to be ReactNode

Perhaps redundant prop:

  • icon: i suggest for now to import the plus icon directly to the component, instead of passing it down. Since we have kept a design pattern (mostly) where inline toggle form elements have a plus icon when in view mode. What do you think?

Comment on lines 40 to 43
const handleClick = (): void => {
setInputValue(value);
setIsEditMode(true);
};
Copy link
Contributor

Choose a reason for hiding this comment

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

handleClick is a bit vague in this context, since there is several clickable actions in this file. What about something like openEditMode?

And is there a reason to have setInputValue(value) in this function?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

we need setInputValue(value) to make sure the value is reseted after reset changes.
in this video when i comment setInputValue(value) the the value "test" still shows in field even i reset changes.

Screen.Recording.2026-02-03.at.15.31.55.mov

Comment on lines 47 to 59
<StudioButton
variant='tertiary'
className={classes.viewMode}
onClick={handleClick}
aria-label={label}
icon={!value && icon}
>
<div className={toggleViewModeContent}>
<StudioLabel>{label}</StudioLabel>
<StudioParagraph>{value}</StudioParagraph>
</div>
{value && <StudioEditIcon />}
</StudioButton>
Copy link
Contributor

Choose a reason for hiding this comment

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

This a good solution, but we already have a studio component to solve this: StudioProperty.Button.
Here is an example where it used somewhere else:

      <StudioProperty.Button
        onClick={() => setEditMode(true)}
        property={property}
        title={title}
        value={value}
        className={className}
      />

Comment on lines 87 to 93
<StudioButton icon={<StudioSaveIcon />} onClick={saveValue} aria-label={saveAriaLabel} />
<StudioButton
variant='secondary'
icon={<StudioCancelIcon />}
onClick={cancelEditing}
aria-label={cancelAriaLabel}
/>
Copy link
Contributor

Choose a reason for hiding this comment

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

We could utilize the new studio component StudioFormActions to keep it is easier to maintain and have a consistent design.
Here is an example from somewhere else:

      <StudioFormActions
        className={classes.actionButtons}
        primary={{
          label: t('general.save'),
          onClick: handleSave,
          disabled: isSaveDisabled,
        }}
        secondary={{
          label: t('general.cancel'),
          onClick: handleCancel,
        }}
        isLoading={false}
        iconOnly
      />

@JamalAlabdullah
Copy link
Contributor Author

Well done! In this first round of review, I’ve focused on the new Studio component StudioInlineEdit. I’ll do a second review later for the places where the component is used.

The naming StudioInlineEdit is a bit vague to me. It sounds like it could support different input types or render arbitrary children. One idea could be to make it support multiple input types via a prop (as children), but then controlled form handling (handleSave, etc.) would need to be done outside the component. We can consider consolidating that later if/when we need to handle different input types with the same UI.

For now, the component is tightly connected to StudioTextfield and only supports editing a string value. I think giving it a more descriptive name would help. Something like StudioInlineTextField seems clearer for this component. What do you think?

I see there is missing a stories file for this new studio component. That is required for studio components that is custom, as far as I know, to be able to test it in storybook.

Agree 😄 and regarding stories file I wanted actually to wait for it until review is finished to not change again.

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: 1

🤖 Fix all issues with AI agents
In
`@src/Designer/frontend/libs/studio-components/src/components/StudioInlineTextField/StudioInlineTextField.tsx`:
- Around line 75-86: The save and cancel ARIA labels for the icon-only buttons
are optional and may produce unlabeled controls; update the
StudioInlineTextField component so saveAriaLabel and cancelAriaLabel are always
provided: either make the props required on the StudioInlineTextField props
interface, or set sensible defaults (e.g. "Save" and "Cancel") before passing
them into the StudioFormActions primary/secondary props; ensure the
StudioFormActions call uses those non-null labels so the iconOnly buttons have
accessible names.
🧹 Nitpick comments (2)
src/Designer/frontend/app-development/features/appSettings/components/TabsContent/Tabs/AboutTab/AppConfigForm/AppConfigForm.module.css (1)

15-27: Consider removing commented‑out CSS to keep the module tidy.

The commented rules look like leftover experiments and add noise to the stylesheet. If they’re not needed, please delete them to keep the module clean.

🧹 Proposed cleanup
-.inlineTextFieldWithIcon {
-  display: flex;
-  align-items: center;
-  gap: var(--ds-size-3);
-}
-
-/* .inlineTextFieldWithIcon > *:first-child {
-  flex: 1;
-}
-
-.emptyIcon {
-  flex-shrink: 0;
-} */
+.inlineTextFieldWithIcon {
+  display: flex;
+  align-items: center;
+  gap: var(--ds-size-3);
+}
src/Designer/frontend/libs/studio-components/src/components/StudioInlineTextField/StudioInlineTextField.test.tsx (1)

26-44: Consider adding a test for the cancel functionality.

The save flow is well tested, but there's no test verifying that clicking Cancel reverts changes and closes edit mode without calling onChange.

📝 Suggested additional test case
it('should not call onChange and close edit mode when Cancel is clicked', async () => {
  const user = userEvent.setup();
  const onChange = jest.fn();
  renderStudioInlineTextField({
    label,
    value,
    onChange,
    saveAriaLabel: 'Save',
    cancelAriaLabel: 'Cancel',
  });
  await user.click(screen.getByRole('button', { name: label }));
  await user.clear(screen.getByRole('textbox', { name: label }));
  await user.type(screen.getByRole('textbox', { name: label }), 'Discarded value');
  await user.click(screen.getByRole('button', { name: 'Cancel' }));
  expect(onChange).not.toHaveBeenCalled();
  expect(screen.getByRole('button', { name: label })).toBeInTheDocument();
  expect(screen.queryByRole('textbox', { name: label })).not.toBeInTheDocument();
});

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: 1

🤖 Fix all issues with AI agents
In
`@src/Designer/frontend/libs/studio-components/src/components/StudioInlineTextField/StudioInlineTextField.tsx`:
- Around line 40-47: The view-mode root ignores the consumer-supplied className
because StudioProperty.Button is always given only classes.viewMode; update
StudioInlineTextField so when !isEditMode it forwards/merges the incoming
className prop into the StudioProperty.Button call (e.g. combine
classes.viewMode with the component's className using the project's class-merge
utility or simple string concat) so consumers can style the view mode; make the
change in the branch handling isEditMode (where StudioProperty.Button is
rendered) and ensure openEditMode, label, and value are left unchanged.

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

Labels

frontend skip-releasenotes Issues that do not make sense to list in our release notes solution/studio/designer squad/utforming Issues that belongs to the named squad.

Projects

Status: 🔎 In review

Development

Successfully merging this pull request may close these issues.

Replace "valgfrie" felter with the addnew button pattern

2 participants