Repository: obsidian-tasks-group/obsidian-tasks
Pull request #2575 (2024-01-12 12:41:02)

refactor: rewrite TaskLayoutOptions to key on TaskLayoutComponent
# Description

Rewrite `TaskLayoutOptions` to have a container the data as a dictionary.

Done as a pairing session with @claremacrae.

By the end of this refactoring:

* `TaskLayoutOptions` has moved:
    * from
        * `src/TaskLayout.ts`
    * to:
        * `src/Layout/TaskLayoutOptions.ts`
* It now encapsulates some behaviours associated with showing and hiding components, both in serialising/deserialising task lines, and in rendering tasks:
    * Instead of being a collection of unrelated `boolean` values, it now stores a container of `boolean` values, which are looked up with a `TaskLayoutComponent` value
    * this makes it much more convenient to use in the various bits of code that loop over collections of `TaskLayoutComponent` values
* This new encapsulation has simplified the implementation of `TaskLayout` nicely, removing two sets of duplicated data entirely.

## Motivation and Context

- Make adding new layout options easier.
- Make adding new fields to `Task` easier - remove the number of `NEW_TASK_FIELD_EDIT_REQUIRED` locations.
    - One removed from `TaskLayoutOptions`
    - One removed from `TaskLayout.componentsToHideAndGenerateClasses`

## How has this been tested?

- Unit tests.
- Exploratory testing, to confirm that the following still work, and are cancelled by a subsequent `show` command.
    - `hide due date`
    - `hide tags`
    - `hide recurrence rule`
    - `hide backlinks`
    - `hide edit button` 

## Types of changes

- [x] **Refactor** (prefix: `refactor` - non-breaking change which only improves the design or structure of existing code, and making no changes to its external behaviour)
- [x] **Tests** (prefix: `test` - additions and improvements to unit tests and the smoke tests)

## Checklist

- [x] My code follows the code style of this project and passes `yarn run lint`.
- [x] My change has adequate [Unit Test coverage](https://publish.obsidian.md/tasks-contributing/Testing/About+Testing).

## Terms

- [x] My contribution follow this project's [contributing guide](https://publish.obsidian.md/tasks-contributing)
- [x] I agree to follow this project's [Code of Conduct](https://github.com/obsidian-tasks-group/obsidian-tasks/blob/main/CODE_OF_CONDUCT.md)



Patch:
diff --git a/src/IQuery.ts b/src/IQuery.ts
index 0d2bef9e..c2faff6c 100644
--- a/src/IQuery.ts
+++ b/src/IQuery.ts
@@ -1,5 +1,5 @@
+import type { TaskLayoutOptions } from './Layout/TaskLayoutOptions';
 import type { QueryLayoutOptions } from './QueryLayoutOptions';
-import type { TaskLayoutOptions } from './TaskLayout';
 import type { Task } from './Task';
 import type { Grouper } from './Query/Grouper';
 import type { QueryResult } from './Query/QueryResult';
diff --git a/src/InlineRenderer.ts b/src/InlineRenderer.ts
index 135404db..2f325c19 100644
--- a/src/InlineRenderer.ts
+++ b/src/InlineRenderer.ts
@@ -1,9 +1,9 @@
 import type { MarkdownPostProcessorContext, Plugin } from 'obsidian';
 import { MarkdownRenderChild } from 'obsidian';
 import { GlobalFilter } from './Config/GlobalFilter';
+import { TaskLayoutOptions } from './Layout/TaskLayoutOptions';
 import { QueryLayoutOptions } from './QueryLayoutOptions';
 import { Task } from './Task';
-import { TaskLayoutOptions } from './TaskLayout';
 import { TaskLineRenderer } from './TaskLineRenderer';
 import { TaskLocation } from './TaskLocation';
 
diff --git a/src/Layout/TaskLayoutOptions.ts b/src/Layout/TaskLayoutOptions.ts
new file mode 100644
index 00000000..9b9f9821
--- /dev/null
+++ b/src/Layout/TaskLayoutOptions.ts
@@ -0,0 +1,72 @@
+import { type TaskLayoutComponent, defaultLayout } from '../TaskLayout';
+
+/**
+ * Various rendering options of tasks in a query.
+ *
+ * See {@link TaskLayoutComponent} for the available options.
+ *
+ * Note that there is an additional special case, for whether tags are shown.
+ *
+ * @see QueryLayoutOptions
+ */
+export class TaskLayoutOptions {
+    private visible: { [component: string]: boolean } = {};
+    private tagsVisible: boolean = true;
+
+    constructor() {
+        defaultLayout.forEach((component) => {
+            this.visible[component] = true;
+        });
+    }
+
+    public isShown(component: TaskLayoutComponent) {
+        return this.visible[component];
+    }
+
+    public areTagsShown() {
+        return this.tagsVisible;
+    }
+
+    public hide(component: TaskLayoutComponent) {
+        this.visible[component] = false;
+    }
+
+    public setVisibility(component: TaskLayoutComponent, visible: boolean) {
+        this.visible[component] = visible;
+    }
+
+    public setTagsVisibility(visibility: boolean) {
+        this.tagsVisible = visibility;
+    }
+
+    public get shownComponents() {
+        return defaultLayout.filter((component) => {
+            return this.visible[component];
+        });
+    }
+
+    public get hiddenComponents() {
+        return defaultLayout.filter((component) => {
+            return !this.visible[component];
+        });
+    }
+
+    /**
+     * These represent the existing task options, so some components (description & block link for now) are not
+     * here because there are no layout options to remove them.
+     */
+    public get toggleableComponents() {
+        return defaultLayout.filter((component) => {
+            // Description and blockLink are always shown
+            return component !== 'description' && component !== 'blockLink';
+        });
+    }
+
+    public toggleVisibilityExceptDescriptionAndBlockLink() {
+        this.toggleableComponents.forEach((component) => {
+            this.visible[component] = !this.visible[component];
+        });
+
+        this.setTagsVisibility(!this.areTagsShown());
+    }
+}
diff --git a/src/Query/Query.ts b/src/Query/Query.ts
index 750869f0..1bf3864c 100644
--- a/src/Query/Query.ts
+++ b/src/Query/Query.ts
@@ -1,7 +1,7 @@
+import { TaskLayoutOptions } from '../Layout/TaskLayoutOptions';
 import { QueryLayoutOptions } from '../QueryLayoutOptions';
 import { expandPlaceholders } from '../Scripting/ExpandPlaceholders';
 import { makeQueryContext } from '../Scripting/QueryContext';
-import { TaskLayoutOptions } from '../TaskLayout';
 import type { Task } from '../Task';
 import type { IQuery } from '../IQuery';
 import { getSettings } from '../Config/Settings';
@@ -177,7 +177,7 @@ ${source}`;
         return this._taskGroupLimit;
     }
 
-    public get taskLayoutOptions(): TaskLayoutOptions {
+    get taskLayoutOptions(): TaskLayoutOptions {
         return this._taskLayoutOptions;
     }
 
@@ -267,28 +267,28 @@ Problem line: "${line}"`;
                     this._queryLayoutOptions.hidePostponeButton = hide;
                     break;
                 case 'priority':
-                    this._taskLayoutOptions.hidePriority = hide;
+                    this._taskLayoutOptions.setVisibility('priority', !hide);
                     break;
                 case 'cancelled date':
-                    this._taskLayoutOptions.hideCancelledDate = hide;
+                    this._taskLayoutOptions.setVisibility('cancelledDate', !hide);
                     break;
                 case 'created date':
-                    this._taskLayoutOptions.hideCreatedDate = hide;
+                    this._taskLayoutOptions.setVisibility('createdDate', !hide);
                     break;
                 case 'start date':
-                    this._taskLayoutOptions.hideStartDate = hide;
+                    this._taskLayoutOptions.setVisibility('startDate', !hide);
                     break;
                 case 'scheduled date':
-                    this._taskLayoutOptions.hideScheduledDate = hide;
+                    this._taskLayoutOptions.setVisibility('scheduledDate', !hide);
                     break;
                 case 'due date':
-                    this._taskLayoutOptions.hideDueDate = hide;
+                    this._taskLayoutOptions.setVisibility('dueDate', !hide);
                     break;
                 case 'done date':
-                    this._taskLayoutOptions.hideDoneDate = hide;
+                    this._taskLayoutOptions.setVisibility('doneDate', !hide);
                     break;
                 case 'recurrence rule':
-                    this._taskLayoutOptions.hideRecurrenceRule = hide;
+                    this._taskLayoutOptions.setVisibility('recurrenceRule', !hide);
                     break;
                 case 'edit button':
                     this._queryLayoutOptions.hideEditButton = hide;
@@ -297,7 +297,7 @@ Problem line: "${line}"`;
                     this._queryLayoutOptions.hideUrgency = hide;
                     break;
                 case 'tags':
-                    this._taskLayoutOptions.hideTags = hide;
+                    this._taskLayoutOptions.setTagsVisibility(!hide);
                     break;
                 default:
                     this.setError('do not understand hide/show option', line);
diff --git a/src/TaskLayout.ts b/src/TaskLayout.ts
index 860569a0..8aa94fd6 100644
--- a/src/TaskLayout.ts
+++ b/src/TaskLayout.ts
@@ -1,24 +1,6 @@
+import { TaskLayoutOptions } from './Layout/TaskLayoutOptions';
 import { QueryLayoutOptions } from './QueryLayoutOptions';
 
-/**
- * Various rendering options of tasks in a query.
- * See applyOptions below when adding options here.
- *
- * @see QueryLayoutOptions
- */
-export class TaskLayoutOptions {
-    // NEW_TASK_FIELD_EDIT_REQUIRED
-    hidePriority: boolean = false;
-    hideCreatedDate: boolean = false;
-    hideStartDate: boolean = false;
-    hideScheduledDate: boolean = false;
-    hideDoneDate: boolean = false;
-    hideCancelledDate: boolean = false;
-    hideDueDate: boolean = false;
-    hideRecurrenceRule: boolean = false;
-    hideTags: boolean = false;
-}
-
 export type TaskLayoutComponent =
     // NEW_TASK_FIELD_EDIT_REQUIRED
     | 'description'
@@ -73,6 +55,20 @@ function hiddenComponentClassName(component: string) {
     return `tasks-layout-hide-${component}`;
 }
 
+export const defaultLayout: TaskLayoutComponent[] = [
+    // NEW_TASK_FIELD_EDIT_REQUIRED
+    'description',
+    'priority',
+    'recurrenceRule',
+    'createdDate',
+    'startDate',
+    'scheduledDate',
+    'dueDate',
+    'cancelledDate',
+    'doneDate',
+    'blockLink',
+];
+
 /**
  * This represents the desired layout of tasks when they are rendered in a given configuration.
  * The layout is used when flattening the task to a string and when rendering queries, and can be
@@ -80,29 +76,15 @@ function hiddenComponentClassName(component: string) {
  */
 export class TaskLayout extends QueryLayout {
     public shownTaskLayoutComponents(): TaskLayoutComponent[] {
-        return this._shownTaskLayoutComponents;
+        return this.taskLayoutOptions.shownComponents;
     }
     public hiddenTaskLayoutComponents(): TaskLayoutComponent[] {
-        return this._hiddenTaskLayoutComponents;
+        return this.taskLayoutOptions.hiddenComponents;
     }
     public taskListHiddenClasses(): string[] {
         return this._taskListHiddenClasses;
     }
-    public defaultLayout: TaskLayoutComponent[] = [
-        // NEW_TASK_FIELD_EDIT_REQUIRED
-        'description',
-        'priority',
-        'recurrenceRule',
-        'createdDate',
-        'startDate',
-        'scheduledDate',
-        'dueDate',
-        'cancelledDate',
-        'doneDate',
-        'blockLink',
-    ];
-    private _shownTaskLayoutComponents: TaskLayoutComponent[];
-    private _hiddenTaskLayoutComponents: TaskLayoutComponent[] = [];
+    public defaultLayout: TaskLayoutComponent[] = defaultLayout;
     private taskLayoutOptions: TaskLayoutOptions;
     private _taskListHiddenClasses: string[] = [];
 
@@ -114,8 +96,6 @@ export class TaskLayout extends QueryLayout {
         } else {
             this.taskLayoutOptions = new TaskLayoutOptions();
         }
-
-        this._shownTaskLayoutComponents = this.defaultLayout;
         this.applyOptions();
     }
 
@@ -125,35 +105,15 @@ export class TaskLayout extends QueryLayout {
     }
 
     private applyTaskLayoutOptions() {
-        // Remove components from the layout according to the task options. These represent the existing task options,
-        // so some components (e.g. the description) are not here because there are no layout options to remove them.
-        const componentsToHideAndGenerateClasses: [boolean, TaskLayoutComponent][] = [
-            // NEW_TASK_FIELD_EDIT_REQUIRED
-            [this.taskLayoutOptions.hidePriority, 'priority'],
-            [this.taskLayoutOptions.hideRecurrenceRule, 'recurrenceRule'],
-            [this.taskLayoutOptions.hideCreatedDate, 'createdDate'],
-            [this.taskLayoutOptions.hideStartDate, 'startDate'],
-            [this.taskLayoutOptions.hideScheduledDate, 'scheduledDate'],
-            [this.taskLayoutOptions.hideDueDate, 'dueDate'],
-            [this.taskLayoutOptions.hideCancelledDate, 'cancelledDate'],
-            [this.taskLayoutOptions.hideDoneDate, 'doneDate'],
-        ];
-        for (const [hide, component] of componentsToHideAndGenerateClasses) {
-            this.hideComponent(hide, component);
-            generateHiddenClassForTaskList(this._taskListHiddenClasses, hide, component);
-        }
+        this.taskLayoutOptions.toggleableComponents.forEach((component) => {
+            generateHiddenClassForTaskList(
+                this._taskListHiddenClasses,
+                !this.taskLayoutOptions.isShown(component),
+                component,
+            );
+        });
 
         // Tags are hidden, rather than removed. See tasks-layout-hide-tags in styles.css.
-        generateHiddenClassForTaskList(this._taskListHiddenClasses, this.taskLayoutOptions.hideTags, 'tags');
-    }
-
-    /**
-     * Move a component from the shown to hidden if the given layoutOption criteria is met.
-     */
-    private hideComponent(hide: boolean, component: TaskLayoutComponent) {
-        if (hide) {
-            this._hiddenTaskLayoutComponents.push(component);
-            this._shownTaskLayoutComponents = this._shownTaskLayoutComponents.filter((element) => element != component);
-        }
+        generateHiddenClassForTaskList(this._taskListHiddenClasses, !this.taskLayoutOptions.areTagsShown(), 'tags');
     }
 }
diff --git a/src/TaskLineRenderer.ts b/src/TaskLineRenderer.ts
index 297ad89a..cb849b19 100644
--- a/src/TaskLineRenderer.ts
+++ b/src/TaskLineRenderer.ts
@@ -3,12 +3,12 @@ import { Component, MarkdownRenderer } from 'obsidian';
 import { GlobalFilter } from './Config/GlobalFilter';
 import { TASK_FORMATS, getSettings } from './Config/Settings';
 import { replaceTaskWithTasks } from './File';
+import type { TaskLayoutOptions } from './Layout/TaskLayoutOptions';
 import type { QueryLayoutOptions } from './QueryLayoutOptions';
 import type { Task } from './Task';
 import * as taskModule from './Task';
 import { TaskFieldRenderer } from './TaskFieldRenderer';
-import type { TaskLayoutComponent, TaskLayoutOptions } from './TaskLayout';
-import { TaskLayout } from './TaskLayout';
+import type { TaskLayoutComponent } from './TaskLayout';
 import { StatusMenu } from './ui/Menus/StatusMenu';
 import { StatusRegistry } from './StatusRegistry';
 
@@ -50,7 +50,7 @@ export class TaskLineRenderer {
      *
      * @param parentUlElement HTML element where the task shall be rendered.
      *
-     * @param layoutOptions See {@link TaskLayoutOptions}.
+     * @param taskLayoutOptions See {@link TaskLayoutOptions}.
      *
      * @param queryLayoutOptions See {@link QueryLayoutOptions}.
      */
@@ -154,10 +154,9 @@ export class TaskLineRenderer {
 
     private async taskToHtml(task: Task, parentElement: HTMLElement, li: HTMLLIElement): Promise<void> {
         const fieldRenderer = new TaskFieldRenderer();
-        const taskLayout = new TaskLayout(this.taskLayoutOptions, this.queryLayoutOptions);
         const emojiSerializer = TASK_FORMATS.tasksPluginEmoji.taskSerializer;
         // Render and build classes for all the task's visible components
-        for (const component of taskLayout.shownTaskLayoutComponents()) {
+        for (const component of this.taskLayoutOptions.shownComponents) {
             const componentString = emojiSerializer.componentToString(
                 task,
                 this.queryLayoutOptions.shortMode,
@@ -187,7 +186,7 @@ export class TaskLineRenderer {
         }
 
         // Now build classes for the hidden task components without rendering them
-        for (const component of taskLayout.hiddenTaskLayoutComponents()) {
+        for (const component of this.taskLayoutOptions.hiddenComponents) {
             fieldRenderer.addDataAttribute(li, task, component);
         }
 
diff --git a/src/TaskSerializer/DefaultTaskSerializer.ts b/src/TaskSerializer/DefaultTaskSerializer.ts
index d326d339..3e1e9f3a 100644
--- a/src/TaskSerializer/DefaultTaskSerializer.ts
+++ b/src/TaskSerializer/DefaultTaskSerializer.ts
@@ -1,5 +1,5 @@
 import type { Moment } from 'moment';
-import { TaskLayout } from '../TaskLayout';
+import { TaskLayoutOptions } from '../Layout/TaskLayoutOptions';
 import type { TaskLayoutComponent } from '../TaskLayout';
 import { Recurrence } from '../Recurrence';
 import { Priority, Task, TaskRegularExpressions } from '../Task';
@@ -85,10 +85,10 @@ export class DefaultTaskSerializer implements TaskSerializer {
      * @return The string representation of the task
      */
     public serialize(task: Task): string {
-        const taskLayout = new TaskLayout();
+        const taskLayoutOptions = new TaskLayoutOptions();
         let taskString = '';
         const shortMode = false;
-        for (const component of taskLayout.shownTaskLayoutComponents()) {
+        for (const component of taskLayoutOptions.shownComponents) {
             taskString += this.componentToString(task, shortMode, component);
         }
         return taskString;


Test patch:
diff --git a/tests/Layout/TaskLayoutOptions.test.ts b/tests/Layout/TaskLayoutOptions.test.ts
new file mode 100644
index 00000000..7df4a85b
--- /dev/null
+++ b/tests/Layout/TaskLayoutOptions.test.ts
@@ -0,0 +1,141 @@
+import { TaskLayoutOptions } from '../../src/Layout/TaskLayoutOptions';
+
+describe('TaskLayoutOptions', () => {
+    it('should be constructable', () => {
+        const options = new TaskLayoutOptions();
+        expect(options).not.toBeNull();
+
+        expect(options.shownComponents.join('\n')).toMatchInlineSnapshot(`
+            "description
+            priority
+            recurrenceRule
+            createdDate
+            startDate
+            scheduledDate
+            dueDate
+            cancelledDate
+            doneDate
+            blockLink"
+        `);
+
+        expect(options.areTagsShown()).toEqual(true);
+    });
+
+    it('should show fields by default', () => {
+        const options = new TaskLayoutOptions();
+
+        expect(options.isShown('priority')).toEqual(true);
+        expect(options.isShown('createdDate')).toEqual(true);
+    });
+
+    it('should be able to hide a field', () => {
+        const options = new TaskLayoutOptions();
+        options.hide('createdDate');
+
+        expect(options.isShown('createdDate')).toEqual(false);
+    });
+
+    it('should be settable via a boolean', () => {
+        const options = new TaskLayoutOptions();
+
+        options.setVisibility('scheduledDate', false);
+        expect(options.isShown('scheduledDate')).toEqual(false);
+
+        options.setVisibility('scheduledDate', true);
+        expect(options.isShown('scheduledDate')).toEqual(true);
+    });
+
+    it('should set tag visibility', () => {
+        const options = new TaskLayoutOptions();
+        expect(options.areTagsShown()).toEqual(true);
+
+        options.setTagsVisibility(false);
+        expect(options.areTagsShown()).toEqual(false);
+
+        options.setTagsVisibility(true);
+        expect(options.areTagsShown()).toEqual(true);
+    });
+
+    it('should provide a list of shown components', () => {
+        const options = new TaskLayoutOptions();
+        expect(options.shownComponents.join('\n')).toMatchInlineSnapshot(`
+            "description
+            priority
+            recurrenceRule
+            createdDate
+            startDate
+            scheduledDate
+            dueDate
+            cancelledDate
+            doneDate
+            blockLink"
+        `);
+
+        options.setVisibility('dueDate', false);
+        options.setVisibility('blockLink', false);
+
+        expect(options.shownComponents.join('\n')).toMatchInlineSnapshot(`
+            "description
+            priority
+            recurrenceRule
+            createdDate
+            startDate
+            scheduledDate
+            cancelledDate
+            doneDate"
+        `);
+    });
+
+    it('should provide a list of hidden components', () => {
+        const options = new TaskLayoutOptions();
+        expect(options.hiddenComponents.join('\n')).toMatchInlineSnapshot('""');
+
+        options.setVisibility('startDate', false);
+        options.setVisibility('doneDate', false);
+
+        expect(options.hiddenComponents.join('\n')).toMatchInlineSnapshot(`
+            "startDate
+            doneDate"
+        `);
+    });
+
+    it('should toggle visibility', () => {
+        const options = new TaskLayoutOptions();
+
+        options.setVisibility('cancelledDate', false);
+        options.setVisibility('priority', true);
+        options.setTagsVisibility(true);
+
+        options.toggleVisibilityExceptDescriptionAndBlockLink();
+
+        expect(options.isShown('cancelledDate')).toEqual(true);
+        expect(options.isShown('priority')).toEqual(false);
+        expect(options.areTagsShown()).toEqual(false);
+    });
+
+    it('should not toggle visibility of description and blockLink', () => {
+        const options = new TaskLayoutOptions();
+        options.setVisibility('description', true);
+        options.setVisibility('blockLink', true);
+
+        options.toggleVisibilityExceptDescriptionAndBlockLink();
+
+        expect(options.isShown('description')).toEqual(true);
+        expect(options.isShown('blockLink')).toEqual(true);
+    });
+
+    it('should provide toggleable components', () => {
+        const options = new TaskLayoutOptions();
+
+        expect(options.toggleableComponents.join('\n')).toMatchInlineSnapshot(`
+            "priority
+            recurrenceRule
+            createdDate
+            startDate
+            scheduledDate
+            dueDate
+            cancelledDate
+            doneDate"
+        `);
+    });
+});
diff --git a/tests/TaskLayout.test.ts b/tests/TaskLayout.test.ts
index f83c6e51..41134010 100644
--- a/tests/TaskLayout.test.ts
+++ b/tests/TaskLayout.test.ts
@@ -2,8 +2,9 @@
  * @jest-environment jsdom
  */
 
+import { TaskLayoutOptions } from '../src/Layout/TaskLayoutOptions';
 import { QueryLayoutOptions } from '../src/QueryLayoutOptions';
-import { TaskLayout, TaskLayoutOptions } from '../src/TaskLayout';
+import { TaskLayout } from '../src/TaskLayout';
 
 describe('TaskLayout tests', () => {
     it('should generate expected CSS components for default layout', () => {
@@ -25,12 +26,8 @@ describe('TaskLayout tests', () => {
     });
 
     it('should generate expected CSS components with all default option reversed', () => {
-        const layoutOptions = new TaskLayoutOptions();
-        // Negate all the task layout boolean values:
-        Object.keys(layoutOptions).forEach((key) => {
-            const key2 = key as keyof TaskLayoutOptions;
-            layoutOptions[key2] = !layoutOptions[key2];
-        });
+        const taskLayoutOptions = new TaskLayoutOptions();
+        taskLayoutOptions.toggleVisibilityExceptDescriptionAndBlockLink();
 
         const queryLayoutOptions = new QueryLayoutOptions();
         // Negate all the query layout boolean values:
@@ -39,7 +36,7 @@ describe('TaskLayout tests', () => {
             queryLayoutOptions[key2] = !queryLayoutOptions[key2];
         });
 
-        const taskLayout = new TaskLayout(layoutOptions, queryLayoutOptions);
+        const taskLayout = new TaskLayout(taskLayoutOptions, queryLayoutOptions);
 
         expect(taskLayout.shownTaskLayoutComponents().join('\n')).toMatchInlineSnapshot(`
             "description
diff --git a/tests/TaskLineRenderer.test.ts b/tests/TaskLineRenderer.test.ts
index e082fa23..5c54ceae 100644
--- a/tests/TaskLineRenderer.test.ts
+++ b/tests/TaskLineRenderer.test.ts
@@ -5,11 +5,12 @@ import moment from 'moment';
 import { DebugSettings } from '../src/Config/DebugSettings';
 import { GlobalFilter } from '../src/Config/GlobalFilter';
 import { resetSettings, updateSettings } from '../src/Config/Settings';
+import { TaskLayoutOptions } from '../src/Layout/TaskLayoutOptions';
 import { DateParser } from '../src/Query/DateParser';
 import { QueryLayoutOptions } from '../src/QueryLayoutOptions';
 import type { Task } from '../src/Task';
 import { TaskRegularExpressions } from '../src/Task';
-import { TaskLayoutOptions } from '../src/TaskLayout';
+import type { TaskLayoutComponent } from '../src/TaskLayout';
 import type { TextRenderer } from '../src/TaskLineRenderer';
 import { TaskLineRenderer } from '../src/TaskLineRenderer';
 import { fromLine } from './TestHelpers';
@@ -24,7 +25,7 @@ window.moment = moment;
  *
  * @param task to be rendered
  *
- * @param layoutOptions for the task rendering. Skip for default options. See {@link TaskLayoutOptions}.
+ * @param taskLayoutOptions for the task rendering. Skip for default options. See {@link TaskLayoutOptions}.
  *
  * @param testRenderer imitates Obsidian rendering. Skip for the default {@link mockTextRenderer}.
  *
@@ -32,7 +33,7 @@ window.moment = moment;
  */
 async function renderListItem(
     task: Task,
-    layoutOptions?: TaskLayoutOptions,
+    taskLayoutOptions?: TaskLayoutOptions,
     queryLayoutOptions?: QueryLayoutOptions,
     testRenderer?: TextRenderer,
 ) {
@@ -40,7 +41,7 @@ async function renderListItem(
         textRenderer: testRenderer ?? mockTextRenderer,
         obsidianComponent: null,
         parentUlElement: document.createElement('div'),
-        taskLayoutOptions: layoutOptions ?? new TaskLayoutOptions(),
+        taskLayoutOptions: taskLayoutOptions ?? new TaskLayoutOptions(),
         queryLayoutOptions: queryLayoutOptions ?? new QueryLayoutOptions(),
     });
     return await taskLineRenderer.renderTaskLine(task, 0);
@@ -175,10 +176,13 @@ describe('task line rendering - global filter', () => {
 });
 
 describe('task line rendering - layout options', () => {
-    const testLayoutOptions = async (expectedComponents: string[], layoutOptions: Partial<TaskLayoutOptions>) => {
+    const testLayoutOptions = async (expectedComponents: string[], hiddenComponents: TaskLayoutComponent[]) => {
         const task = TaskBuilder.createFullyPopulatedTask();
-        const fullLayoutOptions = { ...new TaskLayoutOptions(), ...layoutOptions };
-        const listItem = await renderListItem(task, fullLayoutOptions);
+        const taskLayoutOptions = new TaskLayoutOptions();
+        hiddenComponents.forEach((hiddenComponent) => {
+            taskLayoutOptions.hide(hiddenComponent);
+        });
+        const listItem = await renderListItem(task, taskLayoutOptions);
         const renderedComponents = getListItemComponents(listItem);
         expect(renderedComponents).toEqual(expectedComponents);
     };
@@ -197,7 +201,7 @@ describe('task line rendering - layout options', () => {
                 ' ✅ 2023-07-05',
                 ' ^dcf64c',
             ],
-            {},
+            [],
         );
     });
 
@@ -214,7 +218,7 @@ describe('task line rendering - layout options', () => {
                 ' ✅ 2023-07-05',
                 ' ^dcf64c',
             ],
-            { hidePriority: true },
+            ['priority'],
         );
     });
 
@@ -231,7 +235,7 @@ describe('task line rendering - layout options', () => {
                 ' ✅ 2023-07-05',
                 ' ^dcf64c',
             ],
-            { hideRecurrenceRule: true },
+            ['recurrenceRule'],
         );
     });
 
@@ -248,7 +252,7 @@ describe('task line rendering - layout options', () => {
                 ' ✅ 2023-07-05',
                 ' ^dcf64c',
             ],
-            { hideCreatedDate: true },
+            ['createdDate'],
         );
     });
 
@@ -265,7 +269,7 @@ describe('task line rendering - layout options', () => {
                 ' ✅ 2023-07-05',
                 ' ^dcf64c',
             ],
-            { hideStartDate: true },
+            ['startDate'],
         );
     });
 
@@ -282,7 +286,7 @@ describe('task line rendering - layout options', () => {
                 ' ✅ 2023-07-05',
                 ' ^dcf64c',
             ],
-            { hideScheduledDate: true },
+            ['scheduledDate'],
         );
     });
 
@@ -299,7 +303,7 @@ describe('task line rendering - layout options', () => {
                 ' ✅ 2023-07-05',
                 ' ^dcf64c',
             ],
-            { hideDueDate: true },
+            ['dueDate'],
         );
     });
 
@@ -317,7 +321,7 @@ describe('task line rendering - layout options', () => {
                 ' ✅ 2023-07-05',
                 ' ^dcf64c',
             ],
-            {},
+            [],
         );
     });
 
@@ -334,7 +338,7 @@ describe('task line rendering - layout options', () => {
                 ' ❌ 2023-07-06',
                 ' ^dcf64c',
             ],
-            { hideDoneDate: true },
+            ['doneDate'],
         );
     });
 
@@ -351,7 +355,7 @@ describe('task line rendering - layout options', () => {
                 ' ✅ 2023-07-05',
                 ' ^dcf64c',
             ],
-            { hideCancelledDate: true },
+            ['cancelledDate'],
         );
     });
 
@@ -397,17 +401,11 @@ describe('task line rendering - debug info rendering', () => {
 });
 
 describe('task line rendering - classes and data attributes', () => {
-    const testComponentClasses = async (
-        taskLine: string,
-        layoutOptions: Partial<TaskLayoutOptions>,
-        mainClass: string,
-        attributes: string,
-    ) => {
+    const testComponentClasses = async (taskLine: string, mainClass: string, attributes: string) => {
         const task = fromLine({
             line: taskLine,
         });
-        const fullLayoutOptions = { ...new TaskLayoutOptions(), ...layoutOptions };
-        const listItem = await renderListItem(task, fullLayoutOptions);
+        const listItem = await renderListItem(task);
 
         expect(listItem).toHaveAChildSpanWithClassAndDataAttributes(mainClass, attributes);
     };
@@ -415,19 +413,16 @@ describe('task line rendering - classes and data attributes', () => {
     it('should render priority component with its class and data attribute', async () => {
         await testComponentClasses(
             '- [ ] Full task ⏫ ⏳ 2022-07-03 🛫 2022-07-04 🔁 every day',
-            {},
             'task-priority',
             'taskPriority: high',
         );
         await testComponentClasses(
             '- [ ] Full task 🔼 📅 2022-07-02 ⏳ 2022-07-03 🛫 2022-07-04 🔁 every day',
-            {},
             'task-priority',
             'taskPriority: medium',
         );
         await testComponentClasses(
             '- [ ] Full task 🔽 📅 2022-07-02 ⏳ 2022-07-03 🛫 2022-07-04 🔁 every day',
-            {},
             'task-priority',
             'taskPriority: low',
         );
@@ -436,7 +431,6 @@ describe('task line rendering - classes and data attributes', () => {
     it('should render recurrence component with its class and data attribute', async () => {
         await testComponentClasses(
             '- [ ] Full task ⏫ 📅 2022-07-02 ⏳ 2022-07-03 🛫 2022-07-04 🔁 every day',
-            {},
             'task-recurring',
             '',
         );
@@ -444,99 +438,91 @@ describe('task line rendering - classes and data attributes', () => {
 
     it('should render date component with its class and data attribute with "today" value', async () => {
         const today = DateParser.parseDate('today').format(TaskRegularExpressions.dateFormat);
-        await testComponentClasses(`- [ ] Full task ⏫ ➕ ${today}`, {}, 'task-created', 'taskCreated: today');
-        await testComponentClasses(`- [ ] Full task ⏫ 📅 ${today}`, {}, 'task-due', 'taskDue: today');
-        await testComponentClasses(`- [ ] Full task ⏫ ⏳ ${today}`, {}, 'task-scheduled', 'taskScheduled: today');
-        await testComponentClasses(`- [ ] Full task ⏫ 🛫 ${today}`, {}, 'task-start', 'taskStart: today');
-        await testComponentClasses(`- [x] Done task ✅ ${today}`, {}, 'task-done', 'taskDone: today');
-        await testComponentClasses(`- [-] Canc task ❌ ${today}`, {}, 'task-cancelled', 'taskCancelled: today');
+        await testComponentClasses(`- [ ] Full task ⏫ ➕ ${today}`, 'task-created', 'taskCreated: today');
+        await testComponentClasses(`- [ ] Full task ⏫ 📅 ${today}`, 'task-due', 'taskDue: today');
+        await testComponentClasses(`- [ ] Full task ⏫ ⏳ ${today}`, 'task-scheduled', 'taskScheduled: today');
+        await testComponentClasses(`- [ ] Full task ⏫ 🛫 ${today}`, 'task-start', 'taskStart: today');
+        await testComponentClasses(`- [x] Done task ✅ ${today}`, 'task-done', 'taskDone: today');
+        await testComponentClasses(`- [-] Canc task ❌ ${today}`, 'task-cancelled', 'taskCancelled: today');
     });
 
     it('should render date component with its class and data attribute with "future-1d" value', async () => {
         const future = DateParser.parseDate('tomorrow').format(TaskRegularExpressions.dateFormat);
-        await testComponentClasses(`- [ ] Full task ⏫ ➕ ${future}`, {}, 'task-created', 'taskCreated: future-1d');
-        await testComponentClasses(`- [ ] Full task ⏫ 📅 ${future}`, {}, 'task-due', 'taskDue: future-1d');
-        await testComponentClasses(`- [ ] Full task ⏫ ⏳ ${future}`, {}, 'task-scheduled', 'taskScheduled: future-1d');
-        await testComponentClasses(`- [ ] Full task ⏫ 🛫 ${future}`, {}, 'task-start', 'taskStart: future-1d');
-        await testComponentClasses(`- [x] Done task ✅ ${future}`, {}, 'task-done', 'taskDone: future-1d');
-        await testComponentClasses(`- [-] Canc task ❌ ${future}`, {}, 'task-cancelled', 'taskCancelled: future-1d');
+        await testComponentClasses(`- [ ] Full task ⏫ ➕ ${future}`, 'task-created', 'taskCreated: future-1d');
+        await testComponentClasses(`- [ ] Full task ⏫ 📅 ${future}`, 'task-due', 'taskDue: future-1d');
+        await testComponentClasses(`- [ ] Full task ⏫ ⏳ ${future}`, 'task-scheduled', 'taskScheduled: future-1d');
+        await testComponentClasses(`- [ ] Full task ⏫ 🛫 ${future}`, 'task-start', 'taskStart: future-1d');
+        await testComponentClasses(`- [x] Done task ✅ ${future}`, 'task-done', 'taskDone: future-1d');
+        await testComponentClasses(`- [-] Canc task ❌ ${future}`, 'task-cancelled', 'taskCancelled: future-1d');
     });
 
     it('should render date component with its class and data attribute with "future-7d" value', async () => {
         const future = DateParser.parseDate('in 7 days').format(TaskRegularExpressions.dateFormat);
-        await testComponentClasses(`- [ ] Full task ⏫ ➕ ${future}`, {}, 'task-created', 'taskCreated: future-7d');
-        await testComponentClasses(`- [ ] Full task ⏫ 📅 ${future}`, {}, 'task-due', 'taskDue: future-7d');
-        await testComponentClasses(`- [ ] Full task ⏫ ⏳ ${future}`, {}, 'task-scheduled', 'taskScheduled: future-7d');
-        await testComponentClasses(`- [ ] Full task ⏫ 🛫 ${future}`, {}, 'task-start', 'taskStart: future-7d');
-        await testComponentClasses(`- [x] Done task ✅ ${future}`, {}, 'task-done', 'taskDone: future-7d');
-        await testComponentClasses(`- [-] Canc task ❌ ${future}`, {}, 'task-cancelled', 'taskCancelled: future-7d');
+        await testComponentClasses(`- [ ] Full task ⏫ ➕ ${future}`, 'task-created', 'taskCreated: future-7d');
+        await testComponentClasses(`- [ ] Full task ⏫ 📅 ${future}`, 'task-due', 'taskDue: future-7d');
+        await testComponentClasses(`- [ ] Full task ⏫ ⏳ ${future}`, 'task-scheduled', 'taskScheduled: future-7d');
+        await testComponentClasses(`- [ ] Full task ⏫ 🛫 ${future}`, 'task-start', 'taskStart: future-7d');
+        await testComponentClasses(`- [x] Done task ✅ ${future}`, 'task-done', 'taskDone: future-7d');
+        await testComponentClasses(`- [-] Canc task ❌ ${future}`, 'task-cancelled', 'taskCancelled: future-7d');
     });
 
     it('should render date component with its class and data attribute with "past-1d" value', async () => {
         const past = DateParser.parseDate('yesterday').format(TaskRegularExpressions.dateFormat);
-        await testComponentClasses(`- [ ] Full task ⏫ ➕ ${past}`, {}, 'task-created', 'taskCreated: past-1d');
-        await testComponentClasses(`- [ ] Full task ⏫ 📅 ${past}`, {}, 'task-due', 'taskDue: past-1d');
-        await testComponentClasses(`- [ ] Full task ⏫ ⏳ ${past}`, {}, 'task-scheduled', 'taskScheduled: past-1d');
-        await testComponentClasses(`- [ ] Full task ⏫ 🛫 ${past}`, {}, 'task-start', 'taskStart: past-1d');
-        await testComponentClasses(`- [x] Done task ✅ ${past}`, {}, 'task-done', 'taskDone: past-1d');
-        await testComponentClasses(`- [-] Canc task ❌ ${past}`, {}, 'task-cancelled', 'taskCancelled: past-1d');
+        await testComponentClasses(`- [ ] Full task ⏫ ➕ ${past}`, 'task-created', 'taskCreated: past-1d');
+        await testComponentClasses(`- [ ] Full task ⏫ 📅 ${past}`, 'task-due', 'taskDue: past-1d');
+        await testComponentClasses(`- [ ] Full task ⏫ ⏳ ${past}`, 'task-scheduled', 'taskScheduled: past-1d');
+        await testComponentClasses(`- [ ] Full task ⏫ 🛫 ${past}`, 'task-start', 'taskStart: past-1d');
+        await testComponentClasses(`- [x] Done task ✅ ${past}`, 'task-done', 'taskDone: past-1d');
+        await testComponentClasses(`- [-] Canc task ❌ ${past}`, 'task-cancelled', 'taskCancelled: past-1d');
     });
 
     it('should render date component with its class and data attribute with "past-7d" value', async () => {
         const past = DateParser.parseDate('7 days ago').format(TaskRegularExpressions.dateFormat);
-        await testComponentClasses(`- [ ] Full task ⏫ ➕ ${past}`, {}, 'task-created', 'taskCreated: past-7d');
-        await testComponentClasses(`- [ ] Full task ⏫ 📅 ${past}`, {}, 'task-due', 'taskDue: past-7d');
-        await testComponentClasses(`- [ ] Full task ⏫ ⏳ ${past}`, {}, 'task-scheduled', 'taskScheduled: past-7d');
-        await testComponentClasses(`- [ ] Full task ⏫ 🛫 ${past}`, {}, 'task-start', 'taskStart: past-7d');
-        await testComponentClasses(`- [x] Done task ✅ ${past}`, {}, 'task-done', 'taskDone: past-7d');
-        await testComponentClasses(`- [-] Canc task ❌ ${past}`, {}, 'task-cancelled', 'taskCancelled: past-7d');
+        await testComponentClasses(`- [ ] Full task ⏫ ➕ ${past}`, 'task-created', 'taskCreated: past-7d');
+        await testComponentClasses(`- [ ] Full task ⏫ 📅 ${past}`, 'task-due', 'taskDue: past-7d');
+        await testComponentClasses(`- [ ] Full task ⏫ ⏳ ${past}`, 'task-scheduled', 'taskScheduled: past-7d');
+        await testComponentClasses(`- [ ] Full task ⏫ 🛫 ${past}`, 'task-start', 'taskStart: past-7d');
+        await testComponentClasses(`- [x] Done task ✅ ${past}`, 'task-done', 'taskDone: past-7d');
+        await testComponentClasses(`- [-] Canc task ❌ ${past}`, 'task-cancelled', 'taskCancelled: past-7d');
     });
 
     it('should render date component with its class and data attribute with "future-far" & "past-far" values', async () => {
         const future = DateParser.parseDate('in 8 days').format(TaskRegularExpressions.dateFormat);
-        await testComponentClasses(`- [ ] Full task ⏫ ➕ ${future}`, {}, 'task-created', 'taskCreated: future-far');
-        await testComponentClasses(`- [ ] Full task ⏫ 📅 ${future}`, {}, 'task-due', 'taskDue: future-far');
-        await testComponentClasses(
-            `- [ ] Full task ⏫ ⏳ ${future}`,
-            {},
-            'task-scheduled',
-            'taskScheduled: future-far',
-        );
-        await testComponentClasses(`- [ ] Full task ⏫ 🛫 ${future}`, {}, 'task-start', 'taskStart: future-far');
-        await testComponentClasses(`- [x] Done task ✅ ${future}`, {}, 'task-done', 'taskDone: future-far');
-        await testComponentClasses(`- [-] Canc task ❌ ${future}`, {}, 'task-cancelled', 'taskCancelled: future-far');
+        await testComponentClasses(`- [ ] Full task ⏫ ➕ ${future}`, 'task-created', 'taskCreated: future-far');
+        await testComponentClasses(`- [ ] Full task ⏫ 📅 ${future}`, 'task-due', 'taskDue: future-far');
+        await testComponentClasses(`- [ ] Full task ⏫ ⏳ ${future}`, 'task-scheduled', 'taskScheduled: future-far');
+        await testComponentClasses(`- [ ] Full task ⏫ 🛫 ${future}`, 'task-start', 'taskStart: future-far');
+        await testComponentClasses(`- [x] Done task ✅ ${future}`, 'task-done', 'taskDone: future-far');
+        await testComponentClasses(`- [-] Canc task ❌ ${future}`, 'task-cancelled', 'taskCancelled: future-far');
 
         const past = DateParser.parseDate('8 days ago').format(TaskRegularExpressions.dateFormat);
-        await testComponentClasses(`- [ ] Full task ⏫ ➕ ${past}`, {}, 'task-created', 'taskCreated: past-far');
-        await testComponentClasses(`- [ ] Full task ⏫ 📅 ${past}`, {}, 'task-due', 'taskDue: past-far');
-        await testComponentClasses(`- [ ] Full task ⏫ ⏳ ${past}`, {}, 'task-scheduled', 'taskScheduled: past-far');
-        await testComponentClasses(`- [ ] Full task ⏫ 🛫 ${past}`, {}, 'task-start', 'taskStart: past-far');
-        await testComponentClasses(`- [x] Done task ✅ ${past}`, {}, 'task-done', 'taskDone: past-far');
-        await testComponentClasses(`- [-] Canc task ❌ ${past}`, {}, 'task-cancelled', 'taskCancelled: past-far');
+        await testComponentClasses(`- [ ] Full task ⏫ ➕ ${past}`, 'task-created', 'taskCreated: past-far');
+        await testComponentClasses(`- [ ] Full task ⏫ 📅 ${past}`, 'task-due', 'taskDue: past-far');
+        await testComponentClasses(`- [ ] Full task ⏫ ⏳ ${past}`, 'task-scheduled', 'taskScheduled: past-far');
+        await testComponentClasses(`- [ ] Full task ⏫ 🛫 ${past}`, 'task-start', 'taskStart: past-far');
+        await testComponentClasses(`- [x] Done task ✅ ${past}`, 'task-done', 'taskDone: past-far');
+        await testComponentClasses(`- [-] Canc task ❌ ${past}`, 'task-cancelled', 'taskCancelled: past-far');
     });
 
     it('should not add data attributes for invalid dates', async () => {
-        await testComponentClasses('- [ ] task with invalid due date 📅 2023-02-29', {}, 'task-due', '');
+        await testComponentClasses('- [ ] task with invalid due date 📅 2023-02-29', 'task-due', '');
     });
 
     it.each([
-        ['task-priority', 'taskPriority: medium', { hidePriority: true }],
-        ['task-createdDate', 'taskCreated: past-far', { hideCreatedDate: true }],
-        ['task-dueDate', 'taskDue: past-far', { hideDueDate: true }],
-        ['task-scheduledDate', 'taskScheduled: past-far', { hideScheduledDate: true }],
-        ['task-startDate', 'taskStart: past-far', { hideStartDate: true }],
-        ['task-doneDate', 'taskDone: past-far', { hideDoneDate: true }],
-        ['task-cancelledDate', 'taskCancelled: past-far', { hideCancelledDate: true }],
+        ['task-priority', 'taskPriority: medium', 'priority'],
+        ['task-createdDate', 'taskCreated: past-far', 'createdDate'],
+        ['task-dueDate', 'taskDue: past-far', 'dueDate'],
+        ['task-scheduledDate', 'taskScheduled: past-far', 'scheduledDate'],
+        ['task-startDate', 'taskStart: past-far', 'startDate'],
+        ['task-doneDate', 'taskDone: past-far', 'doneDate'],
+        ['task-cancelledDate', 'taskCancelled: past-far', 'cancelledDate'],
     ])(
         'should not render "%s" class but should set "%s" data attributes to the list item',
-        async (
-            expectedAbsentClass: string,
-            expectedDateAttributes: string,
-            layoutOptions: Partial<TaskLayoutOptions>,
-        ) => {
+        async (expectedAbsentClass: string, expectedDateAttributes: string, hiddenComponent: string) => {
             const task = TaskBuilder.createFullyPopulatedTask();
-            const fullLayoutOptions = { ...new TaskLayoutOptions(), ...layoutOptions };
-            const listItem = await renderListItem(task, fullLayoutOptions);
+            const options = new TaskLayoutOptions();
+            options.hide(hiddenComponent as TaskLayoutComponent);
+            const listItem = await renderListItem(task, options);
 
             expect(listItem).not.toHaveAChildSpanWithClass(expectedAbsentClass);
             expect(listItem).toHaveAmongDataAttributes(expectedDateAttributes);
@@ -592,38 +578,25 @@ describe('task line rendering - classes and data attributes', () => {
         expect(tagSpan.dataset.tagName).toEqual('#illegal-data-attribute');
     });
 
-    const testLiAttributes = async (
-        taskLine: string,
-        layoutOptions: Partial<TaskLayoutOptions>,
-        attributes: string[],
-    ) => {
+    const testLiAttributes = async (taskLine: string, attributes: string[]) => {
         const task = fromLine({
             line: taskLine,
         });
-        const fullLayoutOptions = { ...new TaskLayoutOptions(), ...layoutOptions };
-        const listItem = await renderListItem(task, fullLayoutOptions);
+        const listItem = await renderListItem(task);
         for (const attribute of attributes) {
             expect(listItem).toHaveAmongDataAttributes(attribute);
         }
     };
 
     it('creates data attributes for custom statuses', async () => {
-        await testLiAttributes('- [ ] An incomplete task', {}, [
-            'task: ',
-            'taskStatusName: Todo',
-            'taskStatusType: TODO',
-        ]);
-        await testLiAttributes('- [x] A complete task', {}, [
-            'task: x',
-            'taskStatusName: Done',
-            'taskStatusType: DONE',
-        ]);
-        await testLiAttributes('- [/] In-progress task', {}, [
+        await testLiAttributes('- [ ] An incomplete task', ['task: ', 'taskStatusName: Todo', 'taskStatusType: TODO']);
+        await testLiAttributes('- [x] A complete task', ['task: x', 'taskStatusName: Done', 'taskStatusType: DONE']);
+        await testLiAttributes('- [/] In-progress task', [
             'task: /',
             'taskStatusName: In Progress',
             'taskStatusType: IN_PROGRESS',
         ]);
-        await testLiAttributes('- [-] In-progress task', {}, [
+        await testLiAttributes('- [-] In-progress task', [
             'task: -',
             'taskStatusName: Cancelled',
             'taskStatusType: CANCELLED',
@@ -631,7 +604,7 @@ describe('task line rendering - classes and data attributes', () => {
     });
 
     it('marks nonexistent task priority as "normal" priority', async () => {
-        await testLiAttributes('- [ ] Full task 📅 2022-07-02 ⏳ 2022-07-03 🛫 2022-07-04 🔁 every day', {}, [
+        await testLiAttributes('- [ ] Full task 📅 2022-07-02 ⏳ 2022-07-03 🛫 2022-07-04 🔁 every day', [
             'taskPriority: normal',
         ]);
     });
@@ -641,11 +614,14 @@ describe('Visualise HTML', () => {
     async function renderAndVerifyHTML(
         task: Task,
         {
-            layoutOptions,
+            taskLayoutOptions,
             queryLayoutOptions,
-        }: { layoutOptions: TaskLayoutOptions; queryLayoutOptions: QueryLayoutOptions },
+        }: {
+            taskLayoutOptions: TaskLayoutOptions;
+            queryLayoutOptions: QueryLayoutOptions;
+        },
     ) {
-        const listItem = await renderListItem(task, layoutOptions, queryLayoutOptions, mockHTMLRenderer);
+        const listItem = await renderListItem(task, taskLayoutOptions, queryLayoutOptions, mockHTMLRenderer);
 
         const taskAsMarkdown = `<!--
 ${task.toFileLineString()}
@@ -659,25 +635,20 @@ ${task.toFileLineString()}
     const minimalTask = fromLine({ line: '- [-] empty' });
 
     function layoutOptionsFullMode() {
-        const layoutOptions = new TaskLayoutOptions();
-
-        // Show every Task field, disable short mode, do not explain the query
-        // Also note that urgency, backlinks and edit button are rendered in QueryRender.createTaskList(),
-        // so they won't be visible in this test it is using TaskLineRenderer.renderTaskLine().
-        // See also comments in TaskLayout.applyOptions().
-        Object.keys(layoutOptions).forEach((key) => {
-            const key2 = key as keyof TaskLayoutOptions;
-            layoutOptions[key2] = false;
-        });
-
-        return { layoutOptions, queryLayoutOptions: new QueryLayoutOptions() };
+        return {
+            taskLayoutOptions: new TaskLayoutOptions(), // makes the assumption that all the field are shown by default
+            queryLayoutOptions: new QueryLayoutOptions(),
+        };
     }
 
     function layoutOptionsShortMode() {
         const queryLayoutOptions = new QueryLayoutOptions();
         queryLayoutOptions.shortMode = true;
 
-        return { layoutOptions: new TaskLayoutOptions(), queryLayoutOptions };
+        return {
+            taskLayoutOptions: new TaskLayoutOptions(),
+            queryLayoutOptions,
+        };
     }
 
     it('Full task - full mode', async () => {