diff --git a/apps/client/src/services/promoted_attribute_definition_parser.ts b/apps/client/src/services/promoted_attribute_definition_parser.ts index fe3a9cf2e..0d93aae3c 100644 --- a/apps/client/src/services/promoted_attribute_definition_parser.ts +++ b/apps/client/src/services/promoted_attribute_definition_parser.ts @@ -41,6 +41,17 @@ function parse(value: string) { return defObj; } +/** + * For an attribute definition name (e.g. `label:TEST:TEST1`), extracts its type (label) and name (TEST:TEST1). + * @param definitionAttrName the attribute definition name, without the leading `#` (e.g. `label:TEST:TEST1`) + * @return a tuple of [type, name]. + */ +export function extractAttributeDefinitionTypeAndName(definitionAttrName: string): [ "label" | "relation", string ] { + const valueType = definitionAttrName.startsWith("label:") ? "label" : "relation"; + const valueName = definitionAttrName.substring(valueType.length + 1); + return [ valueType, valueName ]; +} + export default { parse }; diff --git a/apps/client/src/test/easy-froca.ts b/apps/client/src/test/easy-froca.ts index e6a9aeaff..bcfa7eb0a 100644 --- a/apps/client/src/test/easy-froca.ts +++ b/apps/client/src/test/easy-froca.ts @@ -87,7 +87,11 @@ export function buildNote(noteDef: NoteDefinition) { let position = 0; for (const [ key, value ] of Object.entries(noteDef)) { const attributeId = utils.randomString(12); - const name = key.substring(1); + let name = key.substring(1); + const isInheritable = key.endsWith("(inheritable)"); + if (isInheritable) { + name = name.substring(0, name.length - "(inheritable)".length); + } let attribute: FAttribute | null = null; if (key.startsWith("#")) { @@ -98,7 +102,7 @@ export function buildNote(noteDef: NoteDefinition) { name, value, position, - isInheritable: false + isInheritable }); } @@ -110,7 +114,7 @@ export function buildNote(noteDef: NoteDefinition) { name, value, position, - isInheritable: false + isInheritable }); } diff --git a/apps/client/src/widgets/PromotedAttributes.tsx b/apps/client/src/widgets/PromotedAttributes.tsx index 42a4f4ace..6565c4ed7 100644 --- a/apps/client/src/widgets/PromotedAttributes.tsx +++ b/apps/client/src/widgets/PromotedAttributes.tsx @@ -5,7 +5,7 @@ import { Attribute } from "../services/attribute_parser"; import FAttribute from "../entities/fattribute"; import clsx from "clsx"; import { t } from "../services/i18n"; -import { DefinitionObject, LabelType } from "../services/promoted_attribute_definition_parser"; +import { DefinitionObject, extractAttributeDefinitionTypeAndName, LabelType } from "../services/promoted_attribute_definition_parser"; import server from "../services/server"; import FNote from "../entities/fnote"; import { ComponentChild, HTMLInputTypeAttribute, InputHTMLAttributes, MouseEventHandler, TargetedEvent, TargetedInputEvent } from "preact"; @@ -78,8 +78,7 @@ function usePromotedAttributeData(note: FNote | null | undefined, componentId: s const cells: Cell[] = []; for (const definitionAttr of promotedDefAttrs) { - const valueType = definitionAttr.name.startsWith("label:") ? "label" : "relation"; - const valueName = definitionAttr.name.substr(valueType.length + 1); + const [ valueType, valueName ] = extractAttributeDefinitionTypeAndName(definitionAttr.name); let valueAttrs = ownedAttributes.filter((el) => el.name === valueName && el.type === valueType) as Attribute[]; diff --git a/apps/client/src/widgets/collections/table/rows.spec.ts b/apps/client/src/widgets/collections/table/rows.spec.ts new file mode 100644 index 000000000..a9269da23 --- /dev/null +++ b/apps/client/src/widgets/collections/table/rows.spec.ts @@ -0,0 +1,26 @@ +import { describe, expect, it } from "vitest"; +import { buildNote } from "../../../test/easy-froca"; +import getAttributeDefinitionInformation from "./rows.js"; + +describe("getAttributeDefinitionInformation", () => { + it("handles attributes with colons in their names", async () => { + const note = buildNote({ + title: "Note 1", + "#label:TEST:TEST1(inheritable)": "promoted,alias=Test1,single,text", + "#label:Test_Test2(inheritable)": "promoted,alias=Test2,single,text", + "#label:TEST:Test3(inheritable)": "promoted,alias=test3,single,text", + "#relation:TEST:TEST4(inheritable)": "promoted,alias=Test4,single", + "#relation:TEST:TEST5(inheritable)": "promoted,alias=Test5,single", + "#label:_TEST:TEST:TEST:Test1(inheritable)": "promoted,alias=Test01,single,text" + }); + const infos = getAttributeDefinitionInformation(note); + expect(infos).toMatchObject([ + { name: "TEST:TEST1", type: "text" }, + { name: "Test_Test2", type: "text" }, + { name: "TEST:Test3", type: "text" }, + { name: "TEST:TEST4", type: "relation" }, + { name: "TEST:TEST5", type: "relation" }, + { name: "_TEST:TEST:TEST:Test1", type: "text" } + ]); + }); +}); diff --git a/apps/client/src/widgets/collections/table/rows.ts b/apps/client/src/widgets/collections/table/rows.ts index e1b0a765e..47530ecbd 100644 --- a/apps/client/src/widgets/collections/table/rows.ts +++ b/apps/client/src/widgets/collections/table/rows.ts @@ -1,5 +1,5 @@ import FNote from "../../../entities/fnote.js"; -import type { LabelType } from "../../../services/promoted_attribute_definition_parser.js"; +import { extractAttributeDefinitionTypeAndName, type LabelType } from "../../../services/promoted_attribute_definition_parser.js"; import type { AttributeDefinitionInformation } from "./columns.js"; export type TableData = { @@ -79,14 +79,14 @@ export default function getAttributeDefinitionInformation(parentNote: FNote) { continue; } - const [ labelType, name ] = attrDef.name.split(":", 2); + const [ attrType, name ] = extractAttributeDefinitionTypeAndName(attrDef.name); if (attrDef.type !== "label") { console.warn("Relations are not supported for now"); continue; } let type: LabelType | "relation" = def.labelType || "text"; - if (labelType === "relation") { + if (attrType === "relation") { type = "relation"; }