Commit 5c7725d5 authored by David Sveningsson's avatar David Sveningsson

feat(meta): allow inheritance

parent 66d75a86
Pipeline #97710790 passed with stages
in 9 minutes and 9 seconds
......@@ -50,6 +50,9 @@ export interface MetaElement {
permittedOrder: PermittedOrder;
requiredAncestors: string[];
requiredContent: string[];
/* inheritance */
inherit?: string;
}
```
......@@ -370,3 +373,33 @@ elements, e.g. global attributes.
]
}
```
## Inheritance
Elements can inherit from other elements using the `inherits` property.
When inheriting all properties will be duplicated to the new element.
Any new property set on the element will override the parent element.
Given the following metadata:
```js
"foo": {
"flow": true,
"transparent": true
},
"bar": {
"inherit": "foo",
"transparent": false
}
```
The final `<bar>` metadata will be merged to:
```js
"bar": {
"flow": true,
"transparent": false
}
```
Elements being inherited must be defined before the inheritor or an error will be thrown.
......@@ -148,8 +148,7 @@ describe("HTML elements", () => {
"html5",
{
"custom-form": {
flow: true,
form: true,
inherit: "form",
},
},
],
......
......@@ -5,6 +5,8 @@
"^.*$": {
"type": "object",
"properties": {
"inherit": { "type": "string" },
"embedded": { "$ref": "#/definitions/contentCategory" },
"flow": { "$ref": "#/definitions/contentCategory" },
"heading": { "$ref": "#/definitions/contentCategory" },
......
......@@ -15,6 +15,9 @@ export interface PermittedAttribute {
}
export interface MetaData {
/* special keyword to extend metadata from another entry */
inherit?: string;
/* content categories */
metadata: boolean | PropertyExpression;
flow: boolean | PropertyExpression;
......
......@@ -306,6 +306,61 @@ describe("MetaTable", () => {
});
});
describe("inheritance", () => {
it("should be supported", () => {
const table = new MetaTable();
table.loadFromObject({
foo: mockEntry({
flow: true,
}),
bar: {
inherit: "foo",
} as MetaData,
});
const bar = table.getMetaFor("bar");
expect(bar).toEqual(
expect.objectContaining({
tagName: "bar",
flow: true,
phrasing: false,
})
);
});
it("should allow overriding", () => {
const table = new MetaTable();
table.loadFromObject({
foo: mockEntry({
flow: true,
phrasing: true,
}),
bar: {
inherit: "foo",
flow: false,
} as MetaData,
});
const bar = table.getMetaFor("bar");
expect(bar).toEqual(
expect.objectContaining({
tagName: "bar",
flow: false,
phrasing: true,
})
);
});
it("should throw error when extending missing element", () => {
const table = new MetaTable();
expect(() => {
table.loadFromObject({
foo: {
inherit: "bar",
} as MetaData,
});
}).toThrow("Element <foo> cannot inherit from <bar>: no such element");
});
});
describe("getTagsWithProperty()", () => {
it("should return list of all tags with given property enabled", () => {
expect.assertions(2);
......
......@@ -130,13 +130,27 @@ export class MetaTable {
}
private addEntry(tagName: string, entry: MetaData): void {
const expanded: MetaElement = Object.assign(
{
tagName,
void: false,
},
entry
) as MetaElement;
const defaultEntry = {
void: false,
};
let parent = {};
/* handle inheritance */
if (entry.inherit) {
const name = entry.inherit;
parent = this.elements[name];
if (!parent) {
throw new UserError(
`Element <${tagName}> cannot inherit from <${name}>: no such element`
);
}
delete entry.inherit;
}
/* merge all sources together */
const expanded: MetaElement = Object.assign(defaultEntry, parent, entry, {
tagName,
}) as MetaElement;
expandRegex(expanded);
this.elements[tagName] = expanded;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment