Skip to content

Commit

Permalink
Merge pull request #2439 from ilandikov/refactor-rendering
Browse files Browse the repository at this point in the history
refactor: improve rendering code quality
  • Loading branch information
claremacrae authored Nov 22, 2023
2 parents 9e89cef + 5f93082 commit cce227f
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 82 deletions.
1 change: 0 additions & 1 deletion src/InlineRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@ export class InlineRenderer {
}

const taskLineRenderer = new TaskLineRenderer({
textRenderer: TaskLineRenderer.obsidianMarkdownRenderer,
obsidianComponent: childComponent,
parentUlElement: element,
layoutOptions: new LayoutOptions(),
Expand Down
1 change: 0 additions & 1 deletion src/QueryRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,6 @@ class QueryRenderChild extends MarkdownRenderChild {
if (groupingAttribute && groupingAttribute.length > 0) taskList.dataset.taskGroupBy = groupingAttribute;

const taskLineRenderer = new TaskLineRenderer({
textRenderer: TaskLineRenderer.obsidianMarkdownRenderer,
obsidianComponent: this,
parentUlElement: taskList,
layoutOptions: this.query.layoutOptions,
Expand Down
24 changes: 15 additions & 9 deletions src/TaskFieldRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ import type { TaskLayoutComponent } from './TaskLayout';
export type AttributesDictionary = { [key: string]: string };

export class FieldLayouts {
private details = FieldLayoutDetails;

constructor() {}
private readonly details = FieldLayoutDetails;

/**
* Searches for the component among the {@link FieldLayoutDetails} and gets its data attribute
Expand All @@ -26,14 +24,22 @@ export class FieldLayouts {
public dataAttribute(component: TaskLayoutComponent, task: Task) {
return this.details[component].dataAttribute(component, task);
}

/**
* @returns the component's CSS class describing what this component is (priority, due date etc.).
* @param component of the task.
*/
public className(component: TaskLayoutComponent) {
return this.details[component].className;
}
}

type AttributeValueCalculator = (component: TaskLayoutComponent, task: Task) => string;

export class FieldLayoutDetail {
readonly className: string;
readonly attributeName: string;
readonly attributeValueCalculator: AttributeValueCalculator;
public readonly className: string;
private readonly attributeName: string;
private readonly attributeValueCalculator: AttributeValueCalculator;

public static noAttributeName = '';
public static noAttributeValueCalculator: AttributeValueCalculator = () => {
Expand Down Expand Up @@ -82,8 +88,8 @@ export class FieldLayoutDetail {
* @param attributeName if the component needs data attribute (`data-key="value"`) this is the key.
* Otherwise, set this to {@link FieldLayoutDetail.noAttributeName}.
*
* @param attributeValueCalculator And this is the value.
* Set to {@link FieldLayoutDetail.noAttributeValueCalculator} if shall be empty.
* @param attributeValueCalculator And this is the value calculator.
* Set to {@link FieldLayoutDetail.noAttributeValueCalculator} if the component has no data attribute.
*
* There is a relation between {@link attributeName} and {@link attributeValueCalculator}.
* For a component to have the data attribute, both need to be set to values other than
Expand Down Expand Up @@ -123,7 +129,7 @@ export class FieldLayoutDetail {
}
}

export const FieldLayoutDetails: { [c in TaskLayoutComponent]: FieldLayoutDetail } = {
const FieldLayoutDetails: { [c in TaskLayoutComponent]: FieldLayoutDetail } = {
// NEW_TASK_FIELD_EDIT_REQUIRED
createdDate: new FieldLayoutDetail('task-created', 'taskCreated', FieldLayoutDetail.dateAttributeCalculator),
dueDate: new FieldLayoutDetail('task-due', 'taskDue', FieldLayoutDetail.dateAttributeCalculator),
Expand Down
27 changes: 12 additions & 15 deletions src/TaskLineRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { TASK_FORMATS, getSettings } from './Config/Settings';
import { replaceTaskWithTasks } from './File';
import type { Task } from './Task';
import * as taskModule from './Task';
import { type AttributesDictionary, FieldLayoutDetails, FieldLayouts } from './TaskFieldRenderer';
import { type AttributesDictionary, FieldLayouts } from './TaskFieldRenderer';
import type { LayoutOptions, TaskLayoutComponent } from './TaskLayout';
import { TaskLayout } from './TaskLayout';

Expand All @@ -22,12 +22,12 @@ export type TextRenderer = (
) => Promise<void>;

export class TaskLineRenderer {
textRenderer: TextRenderer;
obsidianComponent: Component | null;
parentUlElement: HTMLElement;
layoutOptions: LayoutOptions;
private readonly textRenderer: TextRenderer;
private readonly obsidianComponent: Component | null;
private readonly parentUlElement: HTMLElement;
private readonly layoutOptions: LayoutOptions;

static async obsidianMarkdownRenderer(
private static async obsidianMarkdownRenderer(
text: string,
element: HTMLSpanElement,
path: string,
Expand All @@ -40,23 +40,23 @@ export class TaskLineRenderer {
/**
* Builds a renderer for tasks with various options.
*
* @param textRenderer The renderer to be used. For live/prod rendering use {@link TaskLineRenderer.obsidianMarkdownRenderer}.
* @param textRenderer The optional renderer to be used. Skip this parameter for Obsidian rendering.
* For test purposes mock renderers shall be used.
*
* @param obsidianComponent One of the parameters needed by `MarkdownRenderer.renderMarkdown()` Obsidian API,
* that is called by {@link TaskLineRenderer.obsidianMarkdownRenderer}.
* that is called by the Obsidian renderer. Set this to null in test code.
*
* @param parentUlElement HTML element where the task shall be rendered.
*
* @param layoutOptions See {@link LayoutOptions}.
*/
constructor({
textRenderer,
textRenderer = TaskLineRenderer.obsidianMarkdownRenderer,
obsidianComponent,
parentUlElement,
layoutOptions,
}: {
textRenderer: TextRenderer;
textRenderer?: TextRenderer;
obsidianComponent: Component | null;
parentUlElement: HTMLElement;
layoutOptions: LayoutOptions;
Expand Down Expand Up @@ -167,11 +167,8 @@ export class TaskLineRenderer {
this.addInternalClasses(component, internalSpan);

// Add the component's CSS class describing what this component is (priority, due date etc.)
const fieldLayoutDetails = FieldLayoutDetails[component];
if (fieldLayoutDetails) {
const componentClass = [fieldLayoutDetails.className];
span.classList.add(...componentClass);
}
const componentClass = fieldLayouts.className(component);
span.classList.add(...[componentClass]);

// Add the component's attribute ('priority-medium', 'due-past-1d' etc.)
const componentDataAttribute = fieldLayouts.dataAttribute(component, task);
Expand Down
9 changes: 0 additions & 9 deletions tests/TaskFieldRenderer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,6 @@ describe('Field Layout Detail tests', () => {
return '';
});
expect(fieldLayoutDetail.className).toEqual('stuff');
expect(fieldLayoutDetail.attributeName).toEqual('taskAttribute');
});

it('should calculate data attribute value', () => {
const fieldLayoutDetail = new FieldLayoutDetail('foo', 'bar', () => {
return 'someValue';
});
const attributeValue = fieldLayoutDetail.attributeValueCalculator('createdDate', new TaskBuilder().build());
expect(attributeValue).toEqual('someValue');
});

it('should return a data attribute', () => {
Expand Down
Loading

0 comments on commit cce227f

Please sign in to comment.