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