diff --git a/src/app/modules/ix-forms/components/ix-icon-group/ix-icon-group.component.html b/src/app/modules/ix-forms/components/ix-icon-group/ix-icon-group.component.html
index f46d76d80cb..4ab324e2a79 100644
--- a/src/app/modules/ix-forms/components/ix-icon-group/ix-icon-group.component.html
+++ b/src/app/modules/ix-forms/components/ix-icon-group/ix-icon-group.component.html
@@ -6,7 +6,7 @@
 ></ix-label>
 
 <div class="icon-group" [attr.aria-label]="label">
-  @for (option of options | keyvalue; track option) {
+  @for (option of options | keyvalue: keepOrder; track option) {
   <button
     mat-icon-button
     type="button"
diff --git a/src/app/modules/ix-forms/components/ix-icon-group/ix-icon-group.component.spec.ts b/src/app/modules/ix-forms/components/ix-icon-group/ix-icon-group.component.spec.ts
new file mode 100644
index 00000000000..f558aae6d20
--- /dev/null
+++ b/src/app/modules/ix-forms/components/ix-icon-group/ix-icon-group.component.spec.ts
@@ -0,0 +1,94 @@
+import { HarnessLoader } from '@angular/cdk/testing';
+import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
+import { ReactiveFormsModule } from '@angular/forms';
+import { FormControl } from '@ngneat/reactive-forms';
+import { createHostFactory, SpectatorHost } from '@ngneat/spectator/jest';
+import { IxIconGroupComponent } from 'app/modules/ix-forms/components/ix-icon-group/ix-icon-group.component';
+import { IxIconGroupHarness } from 'app/modules/ix-forms/components/ix-icon-group/ix-icon-group.harness';
+import { IxLabelHarness } from 'app/modules/ix-forms/components/ix-label/ix-label.harness';
+import { IxFormsModule } from 'app/modules/ix-forms/ix-forms.module';
+
+describe('IxIconGroupComponent', () => {
+  let spectator: SpectatorHost<IxIconGroupComponent>;
+  let loader: HarnessLoader;
+  let iconGroupHarness: IxIconGroupHarness;
+  const formControl = new FormControl();
+  const createHost = createHostFactory({
+    component: IxIconGroupComponent,
+    imports: [
+      ReactiveFormsModule,
+      IxFormsModule,
+    ],
+    declareComponent: false,
+  });
+
+  beforeEach(async () => {
+    spectator = createHost(
+      `<ix-icon-group
+        [options]="options"
+        [label]="label"
+        [tooltip]="tooltip"
+        [required]="required"
+        [formControl]="formControl"
+      ></ix-icon-group>`,
+      {
+        hostProps: {
+          formControl,
+          options: new Map<string, string>([
+            ['edit', 'mdi-pencil'],
+            ['delete', 'mdi-delete'],
+          ]),
+          label: 'Icon group',
+          tooltip: 'This is a tooltip',
+          required: true,
+        },
+      },
+    );
+
+    loader = TestbedHarnessEnvironment.loader(spectator.fixture);
+    iconGroupHarness = await loader.getHarness(IxIconGroupHarness);
+  });
+
+  describe('rendering', () => {
+    it('renders ix-label and passes label, hint, tooltip and required', async () => {
+      const label = await loader.getHarness(IxLabelHarness.with({ label: 'Icon group' }));
+
+      expect(label).toBeTruthy();
+      expect(await label.isRequired()).toBe(true);
+
+      const tooltip = await label.getTooltip();
+      expect(tooltip).toBeTruthy();
+      expect(await tooltip.getMessage()).toBe('This is a tooltip');
+    });
+
+    it('shows buttons for provided options', async () => {
+      const buttons = await iconGroupHarness.getButtons();
+      expect(buttons).toHaveLength(2);
+
+      const icons = await iconGroupHarness.getIcons();
+      expect(icons).toHaveLength(2);
+      expect(await icons[0].getName()).toBe('mdi-pencil');
+      expect(await icons[1].getName()).toBe('mdi-delete');
+    });
+
+    it('does not highlight any buttons when no value is set', async () => {
+      expect(await iconGroupHarness.getValue()).toBe('');
+    });
+
+    it('highlights button with selected value', async () => {
+      formControl.setValue('edit');
+      expect(await iconGroupHarness.getValue()).toBe('edit');
+    });
+  });
+
+  it('updates form control value when user presses the button', async () => {
+    const buttons = await iconGroupHarness.getButtons();
+    await buttons[1].click();
+    expect(formControl.value).toBe('delete');
+  });
+
+  it('disables buttons when form control is disabled', async () => {
+    formControl.disable();
+    expect(await iconGroupHarness.isDisabled()).toBe(true);
+  });
+});
diff --git a/src/app/modules/ix-forms/components/ix-icon-group/ix-icon-group.component.ts b/src/app/modules/ix-forms/components/ix-icon-group/ix-icon-group.component.ts
index eced223e0c3..1119ed92e29 100644
--- a/src/app/modules/ix-forms/components/ix-icon-group/ix-icon-group.component.ts
+++ b/src/app/modules/ix-forms/components/ix-icon-group/ix-icon-group.component.ts
@@ -14,7 +14,6 @@ import { UntilDestroy } from '@ngneat/until-destroy';
 export class IxIconGroupComponent implements ControlValueAccessor {
   @Input({ required: true }) options: Map<string, string>;
   @Input() label: string;
-  @Input() hint: string;
   @Input() tooltip: string;
   @Input() required: boolean;
 
@@ -53,4 +52,8 @@ export class IxIconGroupComponent implements ControlValueAccessor {
     this.writeValue(value);
     this.onChange(this.value);
   }
+
+  protected keepOrder(): number {
+    return 0;
+  }
 }
diff --git a/src/app/modules/ix-forms/components/ix-icon-group/ix-icon-group.harness.ts b/src/app/modules/ix-forms/components/ix-icon-group/ix-icon-group.harness.ts
index 87637c946ff..d827ad9e434 100644
--- a/src/app/modules/ix-forms/components/ix-icon-group/ix-icon-group.harness.ts
+++ b/src/app/modules/ix-forms/components/ix-icon-group/ix-icon-group.harness.ts
@@ -2,6 +2,7 @@ import {
   BaseHarnessFilters, ComponentHarness, HarnessPredicate, parallel,
 } from '@angular/cdk/testing';
 import { MatButtonHarness } from '@angular/material/button/testing';
+import { MatIconHarness } from '@angular/material/icon/testing';
 import { IxLabelHarness } from 'app/modules/ix-forms/components/ix-label/ix-label.harness';
 import { IxFormControlHarness } from 'app/modules/ix-forms/interfaces/ix-form-control-harness.interface';
 import { getErrorText } from 'app/modules/ix-forms/utils/harness.utils';
@@ -19,6 +20,7 @@ export class IxIconGroupHarness extends ComponentHarness implements IxFormContro
   }
 
   getButtons = this.locatorForAll(MatButtonHarness);
+  getIcons = this.locatorForAll(MatIconHarness.with({ ancestor: '.icon-group' }));
   getErrorText = getErrorText;
 
   async getLabelText(): Promise<string> {
@@ -29,9 +31,13 @@ export class IxIconGroupHarness extends ComponentHarness implements IxFormContro
     return label.getLabel();
   }
 
-  async getValue(): Promise<string> {
-    const selectedButton = this.locatorFor(MatButtonHarness.with({ selector: '.selected' }))();
-    return (await (await selectedButton).host()).getAttribute('aria-label');
+  async getValue(): Promise<string | undefined> {
+    const selectedButton = await this.locatorForOptional(MatButtonHarness.with({ selector: '.selected' }))();
+    if (!selectedButton) {
+      return '';
+    }
+
+    return (await selectedButton.host()).getAttribute('aria-label');
   }
 
   async setValue(value: string): Promise<void> {
diff --git a/src/app/modules/ix-forms/components/ix-icon-group/ix-icon-group.spec.ts b/src/app/modules/ix-forms/components/ix-icon-group/ix-icon-group.spec.ts
deleted file mode 100644
index e726092d70c..00000000000
--- a/src/app/modules/ix-forms/components/ix-icon-group/ix-icon-group.spec.ts
+++ /dev/null
@@ -1,66 +0,0 @@
-import { NgControl } from '@angular/forms';
-import { createComponentFactory, Spectator } from '@ngneat/spectator/jest';
-import { MockComponent } from 'ng-mocks';
-import { IxErrorsComponent } from 'app/modules/ix-forms/components/ix-errors/ix-errors.component';
-import { IxIconGroupComponent } from 'app/modules/ix-forms/components/ix-icon-group/ix-icon-group.component';
-import { IxLabelComponent } from 'app/modules/ix-forms/components/ix-label/ix-label.component';
-
-describe('IxIconGroupComponent', () => {
-  let spectator: Spectator<IxIconGroupComponent>;
-  const createComponent = createComponentFactory({
-    component: IxIconGroupComponent,
-    declarations: [
-      MockComponent(IxLabelComponent),
-      MockComponent(IxErrorsComponent),
-    ],
-    providers: [NgControl],
-  });
-
-  beforeEach(() => {
-    spectator = createComponent();
-    spectator.setInput('options', new Map([
-      ['edit', 'mdi-pencil'],
-      ['delete', 'mdi-delete'],
-    ]));
-  });
-
-  describe('setDisabledState()', () => {
-    it('when called with false, sets \'isDisabled\' to false', () => {
-      spectator.component.setDisabledState(false);
-      expect(spectator.component.isDisabled).toBeFalsy();
-    });
-    it('when called with true, sets \'isDisabled\' to true', () => {
-      spectator.component.setDisabledState(true);
-      expect(spectator.component.isDisabled).toBeTruthy();
-    });
-    it('when called with false, button is not disabled', () => {
-      spectator.component.setDisabledState(false);
-      spectator.detectChanges();
-      expect(spectator.query('button')).not.toBeDisabled();
-    });
-    it('when called with true, button is disabled', () => {
-      spectator.component.setDisabledState(true);
-      spectator.detectChanges();
-      expect(spectator.query('button')).toBeDisabled();
-    });
-  });
-
-  describe('writeValue()', () => {
-    it('when called with edit, sets value to that value', () => {
-      spectator.component.writeValue('edit');
-      expect(spectator.component.value).toBe('edit');
-    });
-
-    it('when called with unexist value, resets value to null', () => {
-      spectator.component.writeValue('unexist');
-      expect(spectator.component.value).toBeNull();
-    });
-  });
-
-  describe('onValueChanged()', () => {
-    it('when called with delete, sets value to be delete', () => {
-      spectator.component.onValueChanged('delete');
-      expect(spectator.component.value).toBe('delete');
-    });
-  });
-});
diff --git a/src/app/modules/ix-forms/components/ix-label/ix-label.harness.ts b/src/app/modules/ix-forms/components/ix-label/ix-label.harness.ts
index 6090a597728..8df4f2c2536 100644
--- a/src/app/modules/ix-forms/components/ix-label/ix-label.harness.ts
+++ b/src/app/modules/ix-forms/components/ix-label/ix-label.harness.ts
@@ -1,6 +1,7 @@
 import {
   BaseHarnessFilters, ComponentHarness, HarnessPredicate,
 } from '@angular/cdk/testing';
+import { IxTooltipHarness } from 'app/modules/tooltip/tooltip.harness';
 
 export interface IxLabelFilters extends BaseHarnessFilters {
   label: string;
@@ -9,6 +10,8 @@ export interface IxLabelFilters extends BaseHarnessFilters {
 export class IxLabelHarness extends ComponentHarness {
   static hostSelector = 'ix-label';
 
+  readonly getTooltip = this.locatorFor(IxTooltipHarness);
+
   static with(options: IxLabelFilters): HarnessPredicate<IxLabelHarness> {
     return new HarnessPredicate(IxLabelHarness, options)
       .addOption('label', options.label, (harness, label) => HarnessPredicate.stringMatches(harness.getLabel(), label));
@@ -22,4 +25,9 @@ export class IxLabelHarness extends ComponentHarness {
 
     return label.text({ exclude: '.required' });
   }
+
+  async isRequired(): Promise<boolean> {
+    const required = await this.locatorForOptional('.required')();
+    return Boolean(required);
+  }
 }
diff --git a/src/app/modules/tooltip/tooltip.harness.ts b/src/app/modules/tooltip/tooltip.harness.ts
new file mode 100644
index 00000000000..0ff9f4a6fe0
--- /dev/null
+++ b/src/app/modules/tooltip/tooltip.harness.ts
@@ -0,0 +1,10 @@
+import { ComponentHarness } from '@angular/cdk/testing';
+
+export class IxTooltipHarness extends ComponentHarness {
+  static hostSelector = 'ix-tooltip';
+
+  async getMessage(): Promise<string> {
+    const message = await this.locatorForOptional('.tooltip-message')();
+    return message ? message.text() : '';
+  }
+}
diff --git a/src/app/pages/dashboard/components/dashboard/dashboard.component.html b/src/app/pages/dashboard/components/dashboard/dashboard.component.html
index 57b06a42937..2885ada18df 100644
--- a/src/app/pages/dashboard/components/dashboard/dashboard.component.html
+++ b/src/app/pages/dashboard/components/dashboard/dashboard.component.html
@@ -64,7 +64,7 @@
           [totalGroups]="renderedGroups().length"
           (moveUp)="onMoveGroup(i, -1)"
           (moveDown)="onMoveGroup(i, 1)"
-          (edit)="onEditGroup(group)"
+          (edit)="onEditGroup(i)"
           (delete)="onDeleteGroup(group)"
         ></ix-widget-group-controls>
 
diff --git a/src/app/pages/dashboard/components/dashboard/dashboard.component.spec.ts b/src/app/pages/dashboard/components/dashboard/dashboard.component.spec.ts
index 39dc626146c..d1aec0bb71d 100644
--- a/src/app/pages/dashboard/components/dashboard/dashboard.component.spec.ts
+++ b/src/app/pages/dashboard/components/dashboard/dashboard.component.spec.ts
@@ -15,7 +15,7 @@ import { WidgetGroupComponent } from 'app/pages/dashboard/components/widget-grou
 import { WidgetGroupFormComponent } from 'app/pages/dashboard/components/widget-group-form/widget-group-form.component';
 import { DashboardStore } from 'app/pages/dashboard/services/dashboard.store';
 import { WidgetGroup, WidgetGroupLayout } from 'app/pages/dashboard/types/widget-group.interface';
-import { IxChainedSlideInService } from 'app/services/ix-chained-slide-in.service';
+import { ChainedComponentResponse, IxChainedSlideInService } from 'app/services/ix-chained-slide-in.service';
 
 describe('DashboardComponent', () => {
   const groupA: WidgetGroup = { layout: WidgetGroupLayout.Full, slots: [] };
@@ -89,6 +89,23 @@ describe('DashboardComponent', () => {
         .toHaveBeenCalledWith(WidgetGroupFormComponent, true, groupA);
     });
 
+    it('updates a widget group after group is edited in WidgetGroupComponent', async () => {
+      const updatedGroup = { ...groupA, layout: WidgetGroupLayout.Halves };
+
+      jest.spyOn(spectator.inject(IxChainedSlideInService), 'open')
+        .mockReturnValue(of({ response: updatedGroup } as ChainedComponentResponse));
+
+      const editIcon = await loader.getHarness(IxIconHarness.with({ name: 'edit' }));
+      await editIcon.click();
+
+      const groups = spectator.queryAll(WidgetGroupComponent);
+      expect(groups).toHaveLength(4);
+      expect(groups[0].group).toEqual(updatedGroup);
+      expect(groups[1].group).toEqual(groupB);
+      expect(groups[2].group).toEqual(groupC);
+      expect(groups[3].group).toEqual(groupD);
+    });
+
     it('removes a widget when delete button is pressed', async () => {
       const deleteIcon = await loader.getHarness(IxIconHarness.with({ name: 'delete' }));
       await deleteIcon.click();
diff --git a/src/app/pages/dashboard/components/dashboard/dashboard.component.ts b/src/app/pages/dashboard/components/dashboard/dashboard.component.ts
index ff7af2997eb..3aee09cd367 100644
--- a/src/app/pages/dashboard/components/dashboard/dashboard.component.ts
+++ b/src/app/pages/dashboard/components/dashboard/dashboard.component.ts
@@ -10,6 +10,7 @@ import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
 import { TranslateService } from '@ngx-translate/core';
 import { EmptyType } from 'app/enums/empty-type.enum';
 import { EmptyConfig } from 'app/interfaces/empty-config.interface';
+import { SnackbarService } from 'app/modules/snackbar/services/snackbar.service';
 import { WidgetGroupFormComponent } from 'app/pages/dashboard/components/widget-group-form/widget-group-form.component';
 import { DashboardStore } from 'app/pages/dashboard/services/dashboard.store';
 import { WidgetGroup } from 'app/pages/dashboard/types/widget-group.interface';
@@ -55,6 +56,7 @@ export class DashboardComponent implements OnInit {
     private slideIn: IxChainedSlideInService,
     private errorHandler: ErrorHandlerService,
     private translate: TranslateService,
+    private snackbar: SnackbarService,
   ) {}
 
   ngOnInit(): void {
@@ -62,7 +64,6 @@ export class DashboardComponent implements OnInit {
     this.loadGroups();
   }
 
-  // TODO: Enter configuration mode. Probably store layout that is being edited in a new service.
   protected onConfigure(): void {
     this.isEditing.set(true);
   }
@@ -77,17 +78,27 @@ export class DashboardComponent implements OnInit {
       .open(WidgetGroupFormComponent, true)
       .pipe(untilDestroyed(this))
       .subscribe((response: ChainedComponentResponse) => {
-        if (response.response) {
-          this.renderedGroups.update((groups) => [...groups, response.response as WidgetGroup]);
+        if (!response.response) {
+          return;
         }
+
+        this.renderedGroups.update((groups) => [...groups, response.response as WidgetGroup]);
       });
   }
 
-  protected onEditGroup(group: WidgetGroup): void {
+  protected onEditGroup(i: number): void {
+    const editedGroup = this.renderedGroups()[i];
     this.slideIn
-      .open(WidgetGroupFormComponent, true, group)
+      .open(WidgetGroupFormComponent, true, editedGroup)
       .pipe(untilDestroyed(this))
-      .subscribe(() => {
+      .subscribe((response) => {
+        if (!response.response) {
+          return;
+        }
+
+        this.renderedGroups.update((groups) => {
+          return groups.map((group, index) => (index === i ? response.response as WidgetGroup : group));
+        });
       });
   }
 
@@ -109,11 +120,13 @@ export class DashboardComponent implements OnInit {
     });
   }
 
-  // TODO: Filter out fully empty groups somewhere.
   protected onSave(): void {
     this.dashboardStore.save(this.renderedGroups())
       .pipe(this.errorHandler.catchError(), untilDestroyed(this))
-      .subscribe(() => this.isEditing.set(false));
+      .subscribe(() => {
+        this.isEditing.set(false);
+        this.snackbar.success(this.translate.instant('Dashboard settings saved'));
+      });
   }
 
   private loadGroups(): void {
diff --git a/src/app/pages/dashboard/components/widget-group-form/widget-group-form.component.scss b/src/app/pages/dashboard/components/widget-group-form/widget-group-form.component.scss
index 1159b60a932..468553f08ef 100644
--- a/src/app/pages/dashboard/components/widget-group-form/widget-group-form.component.scss
+++ b/src/app/pages/dashboard/components/widget-group-form/widget-group-form.component.scss
@@ -3,15 +3,15 @@
   background-color: var(--bg1);
   display: flex;
   flex-direction: column;
+  margin-bottom: 5px;
   margin-left: -20px;
   margin-right: -20px;
-  margin-top: 20px;
   padding: 15px 10px;
 }
 
 .form-row {
   display: flex;
-  gap: 8px;
+  gap: 20px;
 
   ix-select,
   ix-icon-group {
diff --git a/src/app/pages/dashboard/services/dashboard.store.ts b/src/app/pages/dashboard/services/dashboard.store.ts
index ddca1a0ac83..09be33d0d86 100644
--- a/src/app/pages/dashboard/services/dashboard.store.ts
+++ b/src/app/pages/dashboard/services/dashboard.store.ts
@@ -6,7 +6,6 @@ import {
   Observable, catchError, filter, finalize, map, switchMap, tap,
 } from 'rxjs';
 import { WidgetName } from 'app/enums/widget-name.enum';
-import { LoggedInUser } from 'app/interfaces/ds-cache.interface';
 import { demoWidgets } from 'app/pages/dashboard/services/demo-widgets.constant';
 import { WidgetGroup, WidgetGroupLayout } from 'app/pages/dashboard/types/widget-group.interface';
 import { SomeWidgetSettings, WidgetType } from 'app/pages/dashboard/types/widget.interface';
@@ -77,11 +76,11 @@ export class DashboardStore extends ComponentStore<DashboardState> {
     );
   });
 
-  save(groups: WidgetGroup[]): Observable<LoggedInUser> {
+  save(groups: WidgetGroup[]): Observable<void> {
     this.toggleLoadingState(true);
 
     return this.ws.call('auth.set_attribute', ['dashState', groups]).pipe(
-      switchMap(() => this.authService.refetchUser()),
+      switchMap(() => this.authService.refreshUser()),
       finalize(() => this.toggleLoadingState(false)),
     );
   }
diff --git a/src/app/pages/dashboard/types/widget-group.interface.ts b/src/app/pages/dashboard/types/widget-group.interface.ts
index ec994d8dceb..0763772bf7c 100644
--- a/src/app/pages/dashboard/types/widget-group.interface.ts
+++ b/src/app/pages/dashboard/types/widget-group.interface.ts
@@ -48,8 +48,8 @@ export const layoutToSlotSizes = {
 
 export const widgetGroupIcons = new Map<WidgetGroupLayout, string>([
   [WidgetGroupLayout.Full, 'ix:layout_full'],
-  [WidgetGroupLayout.Quarters, 'ix:layout_quarters'],
-  [WidgetGroupLayout.Halves, 'ix:layout_halves'],
   [WidgetGroupLayout.HalfAndQuarters, 'ix:layout_half_and_quarters'],
+  [WidgetGroupLayout.Halves, 'ix:layout_halves'],
   [WidgetGroupLayout.QuartersAndHalf, 'ix:layout_quarters_and_half'],
+  [WidgetGroupLayout.Quarters, 'ix:layout_quarters'],
 ]);
diff --git a/src/app/services/auth/auth.service.ts b/src/app/services/auth/auth.service.ts
index a7b47af19e3..a4962d0a7a3 100644
--- a/src/app/services/auth/auth.service.ts
+++ b/src/app/services/auth/auth.service.ts
@@ -195,10 +195,6 @@ export class AuthService {
     );
   }
 
-  refetchUser(): Observable<LoggedInUser> {
-    return this.getLoggedInUserInformation();
-  }
-
   private processLoginResult(wasLoggedIn: boolean): Observable<LoginResult> {
     return of(wasLoggedIn).pipe(
       switchMap((loggedIn) => {