make XmlAttributeComponent into a generic class
This change brings increased type safety to uses of XmlAttributeComponent. Now the compiler is checkign for us that the properties that get passed in to every subclass match the intended interface, and also that the xmlKeys property -> xml attribute mapping has all the right keys
This commit is contained in:
@ -26,39 +26,30 @@ interface IDocumentAttributesProperties {
|
||||
type?: string;
|
||||
}
|
||||
|
||||
export class DocumentAttributes extends XmlAttributeComponent {
|
||||
|
||||
constructor(properties?: IDocumentAttributesProperties) {
|
||||
super({
|
||||
wpc: "xmlns:wpc",
|
||||
mc: "xmlns:mc",
|
||||
o: "xmlns:o",
|
||||
r: "xmlns:r",
|
||||
m: "xmlns:m",
|
||||
v: "xmlns:v",
|
||||
wp14: "xmlns:wp14",
|
||||
wp: "xmlns:wp",
|
||||
w10: "xmlns:w10",
|
||||
w: "xmlns:w",
|
||||
w14: "xmlns:w14",
|
||||
w15: "xmlns:w15",
|
||||
wpg: "xmlns:wpg",
|
||||
wpi: "xmlns:wpi",
|
||||
wne: "xmlns:wne",
|
||||
wps: "xmlns:wps",
|
||||
Ignorable: "mc:Ignorable",
|
||||
cp: "xmlns:cp",
|
||||
dc: "xmlns:dc",
|
||||
dcterms: "xmlns:dcterms",
|
||||
dcmitype: "xmlns:dcmitype",
|
||||
xsi: "xmlns:xsi",
|
||||
type: "xsi:type",
|
||||
}, properties);
|
||||
|
||||
this.root = properties;
|
||||
|
||||
if (!properties) {
|
||||
this.root = {};
|
||||
}
|
||||
}
|
||||
export class DocumentAttributes extends XmlAttributeComponent<IDocumentAttributesProperties> {
|
||||
protected xmlKeys = {
|
||||
wpc: "xmlns:wpc",
|
||||
mc: "xmlns:mc",
|
||||
o: "xmlns:o",
|
||||
r: "xmlns:r",
|
||||
m: "xmlns:m",
|
||||
v: "xmlns:v",
|
||||
wp14: "xmlns:wp14",
|
||||
wp: "xmlns:wp",
|
||||
w10: "xmlns:w10",
|
||||
w: "xmlns:w",
|
||||
w14: "xmlns:w14",
|
||||
w15: "xmlns:w15",
|
||||
wpg: "xmlns:wpg",
|
||||
wpi: "xmlns:wpi",
|
||||
wne: "xmlns:wne",
|
||||
wps: "xmlns:wps",
|
||||
Ignorable: "mc:Ignorable",
|
||||
cp: "xmlns:cp",
|
||||
dc: "xmlns:dc",
|
||||
dcterms: "xmlns:dcterms",
|
||||
dcmitype: "xmlns:dcmitype",
|
||||
xsi: "xmlns:xsi",
|
||||
type: "xsi:type",
|
||||
};
|
||||
}
|
||||
|
@ -1,18 +1,15 @@
|
||||
import { XmlAttributeComponent, XmlComponent } from "../xml-components";
|
||||
|
||||
interface IndentAttributesProperties {
|
||||
interface IIndentAttributesProperties {
|
||||
left: number;
|
||||
hanging: number;
|
||||
}
|
||||
|
||||
class IndentAttributes extends XmlAttributeComponent {
|
||||
|
||||
constructor(properties: IndentAttributesProperties) {
|
||||
super({
|
||||
left: "w:left",
|
||||
hanging: "w:hanging",
|
||||
}, properties);
|
||||
}
|
||||
class IndentAttributes extends XmlAttributeComponent<IIndentAttributesProperties> {
|
||||
protected xmlKeys = {
|
||||
left: "w:left",
|
||||
hanging: "w:hanging",
|
||||
};
|
||||
}
|
||||
|
||||
export class Indent extends XmlComponent {
|
||||
|
@ -6,14 +6,12 @@ export interface ISpacingProperties {
|
||||
line?: number;
|
||||
}
|
||||
|
||||
class SpacingAttributes extends XmlAttributeComponent {
|
||||
constructor(properties: ISpacingProperties) {
|
||||
super({
|
||||
after: "w:after",
|
||||
before: "w:before",
|
||||
line: "w:line",
|
||||
}, properties);
|
||||
}
|
||||
class SpacingAttributes extends XmlAttributeComponent<ISpacingProperties> {
|
||||
protected xmlKeys = {
|
||||
after: "w:after",
|
||||
before: "w:before",
|
||||
line: "w:line",
|
||||
};
|
||||
}
|
||||
|
||||
export class Spacing extends XmlComponent {
|
||||
|
@ -6,15 +6,12 @@ interface IRunFontAttributesProperties {
|
||||
hint?: string;
|
||||
}
|
||||
|
||||
class RunFontAttributes extends XmlAttributeComponent {
|
||||
|
||||
constructor(properties: IRunFontAttributesProperties) {
|
||||
super({
|
||||
ascii: "w:ascii",
|
||||
hAnsi: "w:hAnsi",
|
||||
hint: "w:hint",
|
||||
}, properties);
|
||||
}
|
||||
class RunFontAttributes extends XmlAttributeComponent<IRunFontAttributesProperties> {
|
||||
protected xmlKeys = {
|
||||
ascii: "w:ascii",
|
||||
hAnsi: "w:hAnsi",
|
||||
hint: "w:hint",
|
||||
};
|
||||
}
|
||||
|
||||
export class RunFonts extends XmlComponent {
|
||||
|
@ -22,29 +22,26 @@ interface IAttributesProperties {
|
||||
pos?: string | number; // Little strange. Perhaps it is normal. Need to clarify in the spec.
|
||||
}
|
||||
|
||||
export class Attributes extends XmlAttributeComponent {
|
||||
|
||||
constructor(properties?: IAttributesProperties) {
|
||||
super({
|
||||
val: "w:val",
|
||||
color: "w:color",
|
||||
space: "w:space",
|
||||
sz: "w:sz",
|
||||
type: "w:type",
|
||||
rsidR: "w:rsidR",
|
||||
rsidRPr: "w:rsidRPr",
|
||||
rsidSect: "w:rsidSect",
|
||||
w: "w:w",
|
||||
h: "w:h",
|
||||
top: "w:top",
|
||||
right: "w:right",
|
||||
bottom: "w:bottom",
|
||||
left: "w:left",
|
||||
header: "w:header",
|
||||
footer: "w:footer",
|
||||
gutter: "w:gutter",
|
||||
linePitch: "w:linePitch",
|
||||
pos: "w:pos",
|
||||
}, properties);
|
||||
}
|
||||
export class Attributes extends XmlAttributeComponent<IAttributesProperties> {
|
||||
protected xmlKeys = {
|
||||
val: "w:val",
|
||||
color: "w:color",
|
||||
space: "w:space",
|
||||
sz: "w:sz",
|
||||
type: "w:type",
|
||||
rsidR: "w:rsidR",
|
||||
rsidRPr: "w:rsidRPr",
|
||||
rsidSect: "w:rsidSect",
|
||||
w: "w:w",
|
||||
h: "w:h",
|
||||
top: "w:top",
|
||||
right: "w:right",
|
||||
bottom: "w:bottom",
|
||||
left: "w:left",
|
||||
header: "w:header",
|
||||
footer: "w:footer",
|
||||
gutter: "w:gutter",
|
||||
linePitch: "w:linePitch",
|
||||
pos: "w:pos",
|
||||
};
|
||||
}
|
||||
|
@ -1,31 +1,25 @@
|
||||
import * as _ from "lodash";
|
||||
import { BaseXmlComponent } from "./base";
|
||||
|
||||
export abstract class XmlAttributeComponent extends BaseXmlComponent {
|
||||
protected root: object;
|
||||
private xmlKeys: object;
|
||||
type AttributeMap<T> = {[P in keyof T]: string};
|
||||
|
||||
constructor(xmlKeys: object, properties: object) {
|
||||
export abstract class XmlAttributeComponent<T> extends BaseXmlComponent {
|
||||
protected root: T;
|
||||
protected xmlKeys: AttributeMap<T>;
|
||||
|
||||
constructor(properties: T) {
|
||||
super("_attr");
|
||||
this.xmlKeys = xmlKeys;
|
||||
|
||||
this.root = properties;
|
||||
|
||||
if (!properties) {
|
||||
this.root = {};
|
||||
}
|
||||
}
|
||||
|
||||
public prepForXml(): {_attr: {[key: string]: (string | number | boolean)}} {
|
||||
const attrs = {};
|
||||
if (this.root !== undefined) {
|
||||
_.forOwn(this.root, (value, key) => {
|
||||
if (value !== undefined) {
|
||||
const newKey = this.xmlKeys[key];
|
||||
attrs[newKey] = value;
|
||||
}
|
||||
});
|
||||
}
|
||||
Object.keys(this.root).forEach((key) => {
|
||||
const value = this.root[key];
|
||||
if (value !== undefined) {
|
||||
const newKey = this.xmlKeys[key];
|
||||
attrs[newKey] = value;
|
||||
}
|
||||
});
|
||||
return {_attr: attrs};
|
||||
}
|
||||
}
|
||||
|
@ -7,14 +7,11 @@ interface IAbstractNumberingAttributesProperties {
|
||||
restartNumberingAfterBreak?: number;
|
||||
}
|
||||
|
||||
class AbstractNumberingAttributes extends XmlAttributeComponent {
|
||||
|
||||
constructor(properties: IAbstractNumberingAttributesProperties) {
|
||||
super({
|
||||
abstractNumId: "w:abstractNumId",
|
||||
restartNumberingAfterBreak: "w15:restartNumberingAfterBreak",
|
||||
}, properties);
|
||||
}
|
||||
class AbstractNumberingAttributes extends XmlAttributeComponent<IAbstractNumberingAttributesProperties> {
|
||||
protected xmlKeys = {
|
||||
abstractNumId: "w:abstractNumId",
|
||||
restartNumberingAfterBreak: "w15:restartNumberingAfterBreak",
|
||||
};
|
||||
}
|
||||
|
||||
export class AbstractNumbering extends XmlComponent {
|
||||
|
@ -7,14 +7,11 @@ interface ILevelAttributesProperties {
|
||||
tentative?: number;
|
||||
}
|
||||
|
||||
class LevelAttributes extends XmlAttributeComponent {
|
||||
|
||||
constructor(properties: ILevelAttributesProperties) {
|
||||
super({
|
||||
ilvl: "w:ilvl",
|
||||
tentative: "w15:tentative",
|
||||
}, properties);
|
||||
}
|
||||
class LevelAttributes extends XmlAttributeComponent<ILevelAttributesProperties> {
|
||||
protected xmlKeys = {
|
||||
ilvl: "w:ilvl",
|
||||
tentative: "w15:tentative",
|
||||
};
|
||||
}
|
||||
|
||||
class Start extends XmlComponent {
|
||||
|
@ -14,13 +14,8 @@ interface INumAttributesProperties {
|
||||
numId: number;
|
||||
}
|
||||
|
||||
class NumAttributes extends XmlAttributeComponent {
|
||||
|
||||
constructor(properties: INumAttributesProperties) {
|
||||
super({
|
||||
numId: "w:numId",
|
||||
}, properties);
|
||||
}
|
||||
class NumAttributes extends XmlAttributeComponent<INumAttributesProperties> {
|
||||
protected xmlKeys = {numId: "w:numId"};
|
||||
}
|
||||
|
||||
export class Num extends XmlComponent {
|
||||
|
@ -4,10 +4,8 @@ interface IComponentAttributes {
|
||||
val: string;
|
||||
}
|
||||
|
||||
class ComponentAttributes extends XmlAttributeComponent {
|
||||
constructor(properties: IComponentAttributes) {
|
||||
super({val: "w:val"}, properties);
|
||||
}
|
||||
class ComponentAttributes extends XmlAttributeComponent<IComponentAttributes> {
|
||||
protected xmlKeys = {val: "w:val"};
|
||||
}
|
||||
|
||||
export class Name extends XmlComponent {
|
||||
|
@ -14,15 +14,13 @@ export interface IStyleAttributes {
|
||||
customStyle?: string;
|
||||
}
|
||||
|
||||
class StyleAttributes extends XmlAttributeComponent {
|
||||
constructor(properties: IStyleAttributes) {
|
||||
super({
|
||||
type: "w:type",
|
||||
styleId: "w:styleId",
|
||||
default: "w:default",
|
||||
customStyle: "w:customStyle",
|
||||
}, properties);
|
||||
}
|
||||
class StyleAttributes extends XmlAttributeComponent<IStyleAttributes> {
|
||||
protected xmlKeys = {
|
||||
type: "w:type",
|
||||
styleId: "w:styleId",
|
||||
default: "w:default",
|
||||
customStyle: "w:customStyle",
|
||||
};
|
||||
}
|
||||
|
||||
export class Style extends XmlComponent {
|
||||
|
@ -2,21 +2,8 @@ import { assert } from "chai";
|
||||
import { Attributes } from "../../../docx/xml-components";
|
||||
|
||||
describe("Attribute", () => {
|
||||
let attributes: Attributes;
|
||||
|
||||
beforeEach(() => {
|
||||
attributes = new Attributes();
|
||||
});
|
||||
|
||||
describe("#constructor()", () => {
|
||||
|
||||
it("should not add val with empty constructor", () => {
|
||||
const newAttrs = new Attributes();
|
||||
const stringifiedJson = JSON.stringify(newAttrs);
|
||||
const newJson = JSON.parse(stringifiedJson);
|
||||
assert.isUndefined(newJson.root.val);
|
||||
});
|
||||
|
||||
it("should have val as defined with populated constructor", () => {
|
||||
const newAttrs = new Attributes({
|
||||
val: "test",
|
||||
|
Reference in New Issue
Block a user