diff --git a/dist/commands/generate.d.ts b/dist/commands/generate.d.ts index 730ea44..bd4c90c 100644 --- a/dist/commands/generate.d.ts +++ b/dist/commands/generate.d.ts @@ -3,7 +3,8 @@ export default class Generate extends Command { static description: string; static flags: { help: import("@oclif/parser/lib/flags").IBooleanFlag; - features: flags.IOptionFlag; + features: flags.IOptionFlag; + outputDir: flags.IOptionFlag; }; run(): Promise; } diff --git a/dist/commands/generate.js b/dist/commands/generate.js index d0674a5..c4a6d94 100644 --- a/dist/commands/generate.js +++ b/dist/commands/generate.js @@ -1,25 +1,35 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const tslib_1 = require("tslib"); -const command_1 = require("@oclif/command"); -const fs = require("fs"); -const generator_1 = require("../generator"); +'use strict' +Object.defineProperty(exports, '__esModule', { value: true }) +const tslib_1 = require('tslib') +const command_1 = require('@oclif/command') +const fs = require('fs') +const html_ui_prototyper_1 = require('../html-ui-prototyper') class Generate extends command_1.Command { - run() { - return tslib_1.__awaiter(this, void 0, void 0, function* () { - const { flags } = this.parse(Generate); - if (flags.features) { - const processResult = JSON.parse(flags.features); - const generator = new generator_1.default(fs); - const result = yield generator.generate(processResult.features); - this.log(JSON.stringify(result)); - } - }); - } + run() { + return tslib_1.__awaiter(this, void 0, void 0, function*() { + const { flags } = this.parse(Generate) + if (flags.features) { + const processResult = JSON.parse(flags.features) + const generator = new html_ui_prototyper_1.default( + fs, + flags.outputDir + ) + const result = yield generator.generate(processResult.features) + this.log(result.join('\n')) + } + }) + } } -Generate.description = 'Generate html files'; +Generate.description = 'Generate html files' Generate.flags = { - help: command_1.flags.help({ char: 'h' }), - features: command_1.flags.string({ description: 'processed features from ast' }) -}; -exports.default = Generate; + help: command_1.flags.help({ char: 'h' }), + features: command_1.flags.string({ + description: 'processed features from ast', + required: true, + }), + outputDir: command_1.flags.string({ + description: 'location where output files will be saved', + required: true, + }), +} +exports.default = Generate diff --git a/dist/generator.js b/dist/generator.js deleted file mode 100644 index 441a542..0000000 --- a/dist/generator.js +++ /dev/null @@ -1,41 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const tslib_1 = require("tslib"); -const fs = require("fs"); -const util_1 = require("util"); -const widget_factory_1 = require("./widgets/widget-factory"); -class Generator { - constructor(_fs = fs) { - this._fs = _fs; - } - generate(features) { - return tslib_1.__awaiter(this, void 0, void 0, function* () { - const factory = new widget_factory_1.default(); - let createFilePromises = []; - for (let feature of features) { - const widgets = []; - for (let element of feature.uiElements) { - const widget = factory.create(element); - widgets.push(widget); - } - const createFilePromise = this.createHtmlFile(feature.name, widgets); - createFilePromises.push(createFilePromise); - } - return Promise.all(createFilePromises); - }); - } - createHtmlFile(fileName, widgets) { - return tslib_1.__awaiter(this, void 0, void 0, function* () { - //TODO format content - let content = widgets.reduce((result, widget) => { - return result + widget.renderToString() + '\n'; - }, ''); - content = `
${content}
`; - const writeF = util_1.promisify(this._fs.writeFile); - const fullName = fileName + '.html'; - yield writeF(fullName, content); - return fullName; - }); - } -} -exports.default = Generator; diff --git a/dist/generator.d.ts b/dist/html-ui-prototyper.d.ts similarity index 50% rename from dist/generator.d.ts rename to dist/html-ui-prototyper.d.ts index d45cbb2..0af34f5 100644 --- a/dist/generator.d.ts +++ b/dist/html-ui-prototyper.d.ts @@ -1,7 +1,9 @@ import { Feature, Prototyper } from 'concordialang-ui-core'; -export default class Generator implements Prototyper { +export default class HtmlUIPrototyper implements Prototyper { private _fs; - constructor(_fs?: any); + private _outputDir; + constructor(_fs: any, _outputDir: string); generate(features: Feature[]): Promise; private createHtmlFile; + private getAppConfig; } diff --git a/dist/html-ui-prototyper.js b/dist/html-ui-prototyper.js new file mode 100644 index 0000000..1a378e9 --- /dev/null +++ b/dist/html-ui-prototyper.js @@ -0,0 +1,62 @@ +'use strict' +Object.defineProperty(exports, '__esModule', { value: true }) +const tslib_1 = require('tslib') +const fs = require('fs') +const util_1 = require('util') +const path_1 = require('path') +const prettier = require('prettier') +const cosmiconfig = require('cosmiconfig') +const widget_factory_1 = require('./widgets/widget-factory') +class HtmlUIPrototyper { + constructor(_fs = fs, _outputDir) { + this._fs = _fs + this._outputDir = _outputDir + } + generate(features) { + return tslib_1.__awaiter(this, void 0, void 0, function*() { + const appConfig = this.getAppConfig() + const factory = new widget_factory_1.default(appConfig) + if (features.length === 0) + return Promise.resolve(['No features found']) + const createFilePromises = [] + for (let feature of features) { + const elements = feature.uiElements.map(uiElement => + factory.create(uiElement) + ) + createFilePromises.push( + this.createHtmlFile(feature.name, elements) + ) + } + return Promise.all(createFilePromises) + }) + } + createHtmlFile(fileName, widgets) { + return tslib_1.__awaiter(this, void 0, void 0, function*() { + let content = widgets.reduce((result, widget) => { + return result + widget.renderToString() + }, '') + content = prettier.format(`
${content}
`, { + parser: 'html', + htmlWhitespaceSensitivity: 'ignore', + }) + const path = path_1.format({ + dir: this._outputDir, + name: fileName, + ext: '.html', + }) + yield util_1.promisify(fs.writeFile)(path, content) + return path + }) + } + getAppConfig() { + try { + const explorer = cosmiconfig() + const configFile = explorer.loadSync('concordialang-ui-html.json') + const appConfig = configFile.config + return appConfig + } catch (e) { + throw new Error('Config file not found') + } + } +} +exports.default = HtmlUIPrototyper diff --git a/dist/index.d.ts b/dist/index.d.ts index 95bc5ff..48bbdaa 100644 --- a/dist/index.d.ts +++ b/dist/index.d.ts @@ -1 +1 @@ -export * from './generator'; +export * from './html-ui-prototyper'; diff --git a/dist/index.js b/dist/index.js index efdaceb..33bdeb0 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1,4 +1,4 @@ 'use strict' Object.defineProperty(exports, '__esModule', { value: true }) const tslib_1 = require('tslib') -tslib_1.__exportStar(require('./generator'), exports) +tslib_1.__exportStar(require('./html-ui-prototyper'), exports) diff --git a/dist/interfaces/app-config.d.ts b/dist/interfaces/app-config.d.ts new file mode 100644 index 0000000..9f58ef3 --- /dev/null +++ b/dist/interfaces/app-config.d.ts @@ -0,0 +1,23 @@ +export interface AppConfig { + widgets?: { + input?: WidgetConfig; + radio?: WidgetConfig; + checkbox?: WidgetConfig; + select?: WidgetConfig; + label?: LabelConfig; + }; +} +export interface WidgetConfig { + opening: string; + closure?: string; + optionOpening?: string; + optionClosure?: string; + wrapperOpening?: string; + wrapperClosure?: string; + label?: LabelConfig; +} +interface LabelConfig { + opening: string; + closure: string; +} +export {}; diff --git a/dist/interfaces/app-config.js b/dist/interfaces/app-config.js new file mode 100644 index 0000000..0a483a3 --- /dev/null +++ b/dist/interfaces/app-config.js @@ -0,0 +1,2 @@ +'use strict' +Object.defineProperty(exports, '__esModule', { value: true }) diff --git a/dist/widgets/prop.d.ts b/dist/utils/prop.d.ts similarity index 63% rename from dist/widgets/prop.d.ts rename to dist/utils/prop.d.ts index c409969..9934699 100644 --- a/dist/widgets/prop.d.ts +++ b/dist/utils/prop.d.ts @@ -1 +1,2 @@ +export declare const PROPS_INJECTION_POINT = "%s"; export declare function formatProperties(props: any, validProperties: string[]): string; diff --git a/dist/utils/prop.js b/dist/utils/prop.js new file mode 100644 index 0000000..b4aede4 --- /dev/null +++ b/dist/utils/prop.js @@ -0,0 +1,27 @@ +'use strict' +Object.defineProperty(exports, '__esModule', { value: true }) +const case_1 = require('case') +exports.PROPS_INJECTION_POINT = '%s' +function formatProperties(props, validProperties) { + const translateProp = key => { + switch (key) { + case 'format': + return 'pattern' + default: + return key + } + } + const getFormattedProp = key => { + let value = case_1.camel(props[key].toString()) + return `${translateProp(key)}="${value}"` + } + const formatValid = (result, prop) => { + return validProperties.includes(prop) + ? result + getFormattedProp(prop) + ' ' + : result + } + return Object.keys(props) + .reduce(formatValid, '') + .trimRight() +} +exports.formatProperties = formatProperties diff --git a/dist/widgets/button.d.ts b/dist/widgets/button.d.ts index 5f9e996..22f036a 100644 --- a/dist/widgets/button.d.ts +++ b/dist/widgets/button.d.ts @@ -1,7 +1,9 @@ import { Widget } from 'concordialang-ui-core'; -export declare class Button extends Widget { +import { WidgetConfig } from '../interfaces/app-config'; +export default class Button extends Widget { + private _config; private readonly VALID_PROPERTIES; - constructor(props: any, name?: string); + constructor(props: any, name: string, _config: WidgetConfig); renderToString(): string; private getType; } diff --git a/dist/widgets/button.js b/dist/widgets/button.js index 80456fa..d77d82d 100644 --- a/dist/widgets/button.js +++ b/dist/widgets/button.js @@ -1,20 +1,29 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const concordialang_ui_core_1 = require("concordialang-ui-core"); -const prop_1 = require("./prop"); +'use strict' +Object.defineProperty(exports, '__esModule', { value: true }) +const concordialang_ui_core_1 = require('concordialang-ui-core') +const prop_1 = require('../utils/prop') class Button extends concordialang_ui_core_1.Widget { - constructor(props, name) { - super(props, name); - // private readonly DATA_TYPES = ['button', 'submit', 'reset'] - this.VALID_PROPERTIES = ['id', 'disabled', 'value']; - } - renderToString() { - const inputType = this.getType(this.props.datatype); - const properties = prop_1.formatProperties(this.props, this.VALID_PROPERTIES); - return ``; - } - getType(datatype) { - return `type="${datatype || 'button'}"`; - } + constructor(props, name, _config) { + super(props, name || '') + this._config = _config + this.VALID_PROPERTIES = ['id', 'disabled', 'value'] + } + renderToString() { + const buttonType = this.getType(this.props.datatype) + let properties = prop_1.formatProperties( + this.props, + this.VALID_PROPERTIES + ) + properties = `${buttonType} ${properties}` + const buttonOpening = this._config.opening.replace( + prop_1.PROPS_INJECTION_POINT, + properties + ) + const buttonClosure = this._config.closure + return buttonOpening + this.name + buttonClosure + } + getType(datatype) { + return `type="${datatype || 'button'}"` + } } -exports.Button = Button; +exports.default = Button diff --git a/dist/widgets/checkbox.d.ts b/dist/widgets/checkbox.d.ts index 16d2703..8128427 100644 --- a/dist/widgets/checkbox.d.ts +++ b/dist/widgets/checkbox.d.ts @@ -1,5 +1,8 @@ import { Widget } from 'concordialang-ui-core'; -export declare class Checkbox extends Widget { - constructor(props: any, name?: string); +import { WidgetConfig } from '../interfaces/app-config'; +export default class Checkbox extends Widget { + private _config; + private readonly VALID_PROPERTIES; + constructor(props: any, name: string, _config: WidgetConfig); renderToString(): string; } diff --git a/dist/widgets/checkbox.js b/dist/widgets/checkbox.js index c9be1d9..4762708 100644 --- a/dist/widgets/checkbox.js +++ b/dist/widgets/checkbox.js @@ -1,12 +1,30 @@ 'use strict' Object.defineProperty(exports, '__esModule', { value: true }) const concordialang_ui_core_1 = require('concordialang-ui-core') +const prop_1 = require('../utils/prop') +const wrapper_1 = require('./wrapper') class Checkbox extends concordialang_ui_core_1.Widget { - constructor(props, name) { + constructor(props, name, _config) { super(props, name) + this._config = _config + this.VALID_PROPERTIES = ['value', 'required'] } renderToString() { - return `TESTE` + const inputType = 'type="checkbox"' + let properties = prop_1.formatProperties( + this.props, + this.VALID_PROPERTIES + ) + properties = `${inputType} ${properties}` + const inputOpening = this._config.opening.replace( + prop_1.PROPS_INJECTION_POINT, + properties + ) + const inputClosure = this._config.closure || '' + return wrapper_1.wrap( + inputOpening + this.name + inputClosure, + this._config + ) } } -exports.Checkbox = Checkbox +exports.default = Checkbox diff --git a/dist/widgets/index.d.ts b/dist/widgets/index.d.ts deleted file mode 100644 index 7fe5a18..0000000 --- a/dist/widgets/index.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './button'; -export * from './input'; -export * from './prop'; -export * from './widget-factory'; diff --git a/dist/widgets/index.js b/dist/widgets/index.js deleted file mode 100644 index 9dfb8f8..0000000 --- a/dist/widgets/index.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict' -Object.defineProperty(exports, '__esModule', { value: true }) -const tslib_1 = require('tslib') -tslib_1.__exportStar(require('./button'), exports) -tslib_1.__exportStar(require('./input'), exports) -tslib_1.__exportStar(require('./prop'), exports) -tslib_1.__exportStar(require('./widget-factory'), exports) diff --git a/dist/widgets/input.d.ts b/dist/widgets/input.d.ts index 8d0d93c..e95736b 100644 --- a/dist/widgets/input.d.ts +++ b/dist/widgets/input.d.ts @@ -1,8 +1,9 @@ import { Widget } from 'concordialang-ui-core'; -export declare class Input extends Widget { +import { WidgetConfig } from '../interfaces/app-config'; +export default class Input extends Widget { + private _config; private readonly VALID_PROPERTIES; - constructor(props: any, name?: string); + constructor(props: any, name: string, _config: WidgetConfig); renderToString(): string; private getType; - private typeForDataType; } diff --git a/dist/widgets/input.js b/dist/widgets/input.js index 2fd88dd..3a81976 100644 --- a/dist/widgets/input.js +++ b/dist/widgets/input.js @@ -1,30 +1,57 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const concordialang_ui_core_1 = require("concordialang-ui-core"); -const prop_1 = require("./prop"); +'use strict' +Object.defineProperty(exports, '__esModule', { value: true }) +const concordialang_ui_core_1 = require('concordialang-ui-core') +const prop_1 = require('../utils/prop') +const label_1 = require('./label') +const wrapper_1 = require('./wrapper') class Input extends concordialang_ui_core_1.Widget { - constructor(props, name) { - super(props, name); - this.VALID_PROPERTIES = ['id', 'editable', 'minlength', 'maxlength', 'required', 'format']; - } - renderToString() { - const inputType = this.getType(this.props.datatype); - const properties = prop_1.formatProperties(this.props, this.VALID_PROPERTIES); - const label = (this.name) ? `` : ''; - return `${label} `; - } - getType(datatype) { - const typeProperty = this.typeForDataType(datatype); - return `type="${typeProperty}"`; - } - typeForDataType(datatype) { - switch (datatype) { - case "integer" /* INTEGER */: - case "double" /* DOUBLE */: return 'number'; - case "time" /* TIME */: return 'time'; - case "datetime" /* DATETIME */: return 'datetime-local'; - } - return 'text'; - } + constructor(props, name, _config) { + super(props, name) + this._config = _config + this.VALID_PROPERTIES = [ + 'id', + 'editable', + 'minlength', + 'maxlength', + 'required', + 'format', + ] + } + renderToString() { + const inputType = this.getType(this.props.datatype) + const properties = prop_1.formatProperties( + this.props, + this.VALID_PROPERTIES + ) + const inputOpening = this._config.opening.replace( + prop_1.PROPS_INJECTION_POINT, + `${inputType} ${properties}` + ) + const inputClosure = this._config.closure || '' + const label = label_1.createLabel( + this.name, + this.props.id.toString(), + this._config + ) + return wrapper_1.wrap(label + inputOpening + inputClosure, this._config) + } + getType(datatype) { + let typeProperty + switch (datatype) { + case 'integer' /* INTEGER */: + case 'double' /* DOUBLE */: + typeProperty = 'number' + break + case 'time' /* TIME */: + typeProperty = 'time' + break + case 'datetime' /* DATETIME */: + typeProperty = 'datetime-local' + break + default: + typeProperty = 'text' + } + return `type="${typeProperty}"` + } } -exports.Input = Input; +exports.default = Input diff --git a/dist/widgets/label.d.ts b/dist/widgets/label.d.ts new file mode 100644 index 0000000..483a09d --- /dev/null +++ b/dist/widgets/label.d.ts @@ -0,0 +1,2 @@ +import { WidgetConfig } from '../interfaces/app-config'; +export declare function createLabel(widgetName: string, widgetId: string, widgetConfig: WidgetConfig): string; diff --git a/dist/widgets/label.js b/dist/widgets/label.js new file mode 100644 index 0000000..5b8099c --- /dev/null +++ b/dist/widgets/label.js @@ -0,0 +1,17 @@ +'use strict' +Object.defineProperty(exports, '__esModule', { value: true }) +const prop_1 = require('../utils/prop') +function createLabel(widgetName, widgetId, widgetConfig) { + if (!widgetConfig.label) return '' + const idPattern = /^(#|~|\d|\w).*/ + const labelFor = widgetId.match(idPattern) + ? `for="${widgetId.replace(/^#|~/, '')}"` + : '' + const labelOpening = widgetConfig.label.opening.replace( + prop_1.PROPS_INJECTION_POINT, + labelFor + ) + const labelClosure = widgetConfig.label.closure + return labelOpening + widgetName + labelClosure +} +exports.createLabel = createLabel diff --git a/dist/widgets/prop.js b/dist/widgets/prop.js deleted file mode 100644 index a394968..0000000 --- a/dist/widgets/prop.js +++ /dev/null @@ -1,12 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -function formatProperties(props, validProperties) { - const getFormattedProp = (key) => `${key}="${props[key]}"`; - const formatValid = function (result, key) { - return validProperties.includes(key) - ? result + getFormattedProp(key) - : result; - }; - return Object.keys(props).reduce(formatValid, ''); -} -exports.formatProperties = formatProperties; diff --git a/dist/widgets/radio.d.ts b/dist/widgets/radio.d.ts new file mode 100644 index 0000000..e7a0a80 --- /dev/null +++ b/dist/widgets/radio.d.ts @@ -0,0 +1,8 @@ +import { Widget } from 'concordialang-ui-core'; +import { WidgetConfig } from '../interfaces/app-config'; +export default class Radio extends Widget { + private _config; + private readonly VALID_PROPERTIES; + constructor(props: any, name: string, _config: WidgetConfig); + renderToString(): string; +} diff --git a/dist/widgets/radio.js b/dist/widgets/radio.js new file mode 100644 index 0000000..c3b4012 --- /dev/null +++ b/dist/widgets/radio.js @@ -0,0 +1,39 @@ +'use strict' +Object.defineProperty(exports, '__esModule', { value: true }) +const concordialang_ui_core_1 = require('concordialang-ui-core') +const prop_1 = require('../utils/prop') +const label_1 = require('./label') +const wrapper_1 = require('./wrapper') +class Radio extends concordialang_ui_core_1.Widget { + constructor(props, name, _config) { + super(props, name) + this._config = _config + this.VALID_PROPERTIES = ['value'] + } + renderToString() { + const inputType = 'type="radio"' + const label = label_1.createLabel(this.name, '', this._config) + let inputs = [] + for (let value of this.props.value) { + // TODO: o que fazer no formatProperties em relação ao value? + // provavelmente terei que instalar o pacote "case" + // para ter 'value="algumaCoisa"', quando value for "Alguma Coisa" + // + // TODO: adicionar propriedades 'id' e 'nome' + const props = Object.assign({}, this.props, { value }) + let properties = prop_1.formatProperties( + props, + this.VALID_PROPERTIES + ) + properties = `${inputType} ${properties}` + const inputOpening = this._config.opening.replace( + prop_1.PROPS_INJECTION_POINT, + properties + ) + const inputClosure = this._config.closure || '' + inputs.push(inputOpening + value + inputClosure) + } + return wrapper_1.wrap(label + inputs.join(''), this._config) + } +} +exports.default = Radio diff --git a/dist/widgets/select.d.ts b/dist/widgets/select.d.ts new file mode 100644 index 0000000..1a81fc7 --- /dev/null +++ b/dist/widgets/select.d.ts @@ -0,0 +1,10 @@ +import { Widget } from 'concordialang-ui-core'; +import { WidgetConfig } from '../interfaces/app-config'; +export default class Select extends Widget { + private _config; + private readonly SELECT_VALID_PROPERTIES; + private readonly OPTION_VALID_PROPERTIES; + constructor(props: any, name: string, _config: WidgetConfig); + renderToString(): string; + private getOptions; +} diff --git a/dist/widgets/select.js b/dist/widgets/select.js new file mode 100644 index 0000000..f748755 --- /dev/null +++ b/dist/widgets/select.js @@ -0,0 +1,52 @@ +'use strict' +Object.defineProperty(exports, '__esModule', { value: true }) +const concordialang_ui_core_1 = require('concordialang-ui-core') +const prop_1 = require('../utils/prop') +const label_1 = require('./label') +const wrapper_1 = require('./wrapper') +class Select extends concordialang_ui_core_1.Widget { + constructor(props, name, _config) { + super(props, name) + this._config = _config + this.SELECT_VALID_PROPERTIES = ['id', 'required'] + this.OPTION_VALID_PROPERTIES = ['value'] + } + renderToString() { + const properties = prop_1.formatProperties( + this.props, + this.SELECT_VALID_PROPERTIES + ) + const selectOpening = this._config.opening.replace( + prop_1.PROPS_INJECTION_POINT, + properties + ) + const selectClosure = this._config.closure + const options = this.getOptions() + const select = selectOpening + options + selectClosure + const label = label_1.createLabel( + this.name, + this.props.id.toString(), + this._config + ) + return wrapper_1.wrap(label + select, this._config) + } + getOptions() { + if (!this._config.optionOpening) return '' + let options = [] + for (let value of this.props.value) { + const optionProps = { value } + const properties = prop_1.formatProperties( + optionProps, + this.OPTION_VALID_PROPERTIES + ) + const optionOpening = this._config.optionOpening.replace( + prop_1.PROPS_INJECTION_POINT, + properties + ) + const optionClosure = this._config.optionClosure + options.push(optionOpening + value + optionClosure) + } + return options.join('') + } +} +exports.default = Select diff --git a/dist/widgets/widget-factory.d.ts b/dist/widgets/widget-factory.d.ts index 18c5714..417a0cf 100644 --- a/dist/widgets/widget-factory.d.ts +++ b/dist/widgets/widget-factory.d.ts @@ -1,4 +1,12 @@ import { UiElement, Widget } from 'concordialang-ui-core'; +import { AppConfig } from '../interfaces/app-config'; export default class WidgetFactory { + private _config; + constructor(_config: AppConfig); create(element: UiElement): Widget; + private createInputElement; + private createRadioElement; + private createCheckboxElement; + private createSelectElement; + private createButtonElement; } diff --git a/dist/widgets/widget-factory.js b/dist/widgets/widget-factory.js index 2543978..c5dfd23 100644 --- a/dist/widgets/widget-factory.js +++ b/dist/widgets/widget-factory.js @@ -1,14 +1,58 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const button_1 = require("./button"); -const input_1 = require("./input"); +'use strict' +Object.defineProperty(exports, '__esModule', { value: true }) +const lodash_1 = require('lodash') +const button_1 = require('./button') +const input_1 = require('./input') +const checkbox_1 = require('./checkbox') +const radio_1 = require('./radio') +const select_1 = require('./select') class WidgetFactory { - create(element) { - switch (element.widget) { - case "textbox" /* TEXTBOX */: return new input_1.Input(element.props, element.name); - case "button" /* BUTTON */: return new button_1.Button(element.props, element.name); - default: return new input_1.Input(null); - } - } + constructor(_config) { + this._config = _config + } + create(element) { + switch (element.widget) { + case 'textbox' /* TEXTBOX */: + return this.createInputElement(element) + case 'button' /* BUTTON */: + return this.createButtonElement(element) + case 'checkbox' /* CHECKBOX */: + return this.createCheckboxElement(element) + case 'radio' /* RADIO */: + return this.createRadioElement(element) + case 'select' /* SELECT */: + return this.createSelectElement(element) + default: + throw new Error(`Invalid widget type: ${element.widget}`) + } + } + createInputElement(element) { + const widgetConfig = lodash_1.get(this._config, 'widgets.input') + widgetConfig.label = + widgetConfig.label || lodash_1.get(this._config, 'widgets.label') + return new input_1.default(element.props, element.name, widgetConfig) + } + createRadioElement(element) { + const widgetConfig = lodash_1.get(this._config, 'widgets.radio') + widgetConfig.label = + widgetConfig.label || lodash_1.get(this._config, 'widgets.label') + return new radio_1.default(element.props, element.name, widgetConfig) + } + createCheckboxElement(element) { + const widgetConfig = lodash_1.get(this._config, 'widgets.checkbox') + widgetConfig.label = + widgetConfig.label || lodash_1.get(this._config, 'widgets.label') + return new checkbox_1.default(element.props, element.name, widgetConfig) + } + createSelectElement(element) { + const widgetConfig = lodash_1.get(this._config, 'widgets.select') + widgetConfig.label = + widgetConfig.label || lodash_1.get(this._config, 'widgets.label') + return new select_1.default(element.props, element.name, widgetConfig) + } + createButtonElement(element) { + const widgetConfig = lodash_1.get(this._config, 'widgets.button') + return new button_1.default(element.props, element.name, widgetConfig) + } } -exports.default = WidgetFactory; +exports.default = WidgetFactory diff --git a/dist/widgets/wrapper.d.ts b/dist/widgets/wrapper.d.ts new file mode 100644 index 0000000..63e9a5b --- /dev/null +++ b/dist/widgets/wrapper.d.ts @@ -0,0 +1,2 @@ +import { WidgetConfig } from '../interfaces/app-config'; +export declare function wrap(elements: string, widgetConfig: WidgetConfig): string; diff --git a/dist/widgets/wrapper.js b/dist/widgets/wrapper.js new file mode 100644 index 0000000..45fa851 --- /dev/null +++ b/dist/widgets/wrapper.js @@ -0,0 +1,10 @@ +'use strict' +Object.defineProperty(exports, '__esModule', { value: true }) +function wrap(elements, widgetConfig) { + if (widgetConfig.wrapperOpening && widgetConfig.wrapperClosure) + return ( + widgetConfig.wrapperOpening + elements + widgetConfig.wrapperClosure + ) + return elements +} +exports.wrap = wrap diff --git a/oclif.manifest.json b/oclif.manifest.json index 59eaeb3..811c12c 100644 --- a/oclif.manifest.json +++ b/oclif.manifest.json @@ -18,7 +18,14 @@ "features": { "name": "features", "type": "option", - "description": "processed features from ast" + "description": "processed features from ast", + "required": true + }, + "outputDir": { + "name": "outputDir", + "type": "option", + "description": "location where output files will be saved", + "required": true } }, "args": [] @@ -40,7 +47,14 @@ "features": { "name": "features", "type": "option", - "description": "processed features from ast" + "description": "processed features from ast", + "required": true + }, + "outputDir": { + "name": "outputDir", + "type": "option", + "description": "location where output files will be saved", + "required": true } }, "args": [] diff --git a/package-lock.json b/package-lock.json index 7d8a9e1..e4ee447 100644 --- a/package-lock.json +++ b/package-lock.json @@ -813,6 +813,11 @@ "integrity": "sha512-yALhelO3i0hqZwhjtcr6dYyaLoCHbAMshwtj6cGxTvHZAKXHsYGdff6E8EPw3xLKY0ELUTQ69Q1rQiJENnccMA==", "dev": true }, + "@types/lodash": { + "version": "4.14.134", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.134.tgz", + "integrity": "sha512-2/O0khFUCFeDlbi7sZ7ZFRCcT812fAeOLm7Ev4KbwASkZ575TDrDcY7YyaoHdTOzKcNbfiwLYZqPmoC4wadrsw==" + }, "@types/node": { "version": "10.14.4", "resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.4.tgz", @@ -860,11 +865,6 @@ "integrity": "sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w==", "dev": true }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, "acorn": { "version": "5.7.3", "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", @@ -967,7 +967,6 @@ "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, "requires": { "sprintf-js": "~1.0.2" } @@ -1255,7 +1254,8 @@ "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true }, "base": { "version": "0.11.2", @@ -1335,6 +1335,7 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1471,7 +1472,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", - "dev": true, "requires": { "callsites": "^2.0.0" }, @@ -1479,8 +1479,7 @@ "callsites": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", - "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", - "dev": true + "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=" } } }, @@ -1488,7 +1487,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", - "dev": true, "requires": { "caller-callsite": "^2.0.0" } @@ -1534,6 +1532,11 @@ "redeyed": "~2.1.0" } }, + "case": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/case/-/case-1.6.1.tgz", + "integrity": "sha512-N0rDB5ftMDKANGsIBRWPWcG0VIKtirgqcXb2vKFi66ySAjXVEwbfCN7ass1mkdXO8fbol3RfbWlQ9KyBX2F/Gg==" + }, "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", @@ -1854,54 +1857,18 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true }, "concordialang-ui-core": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/concordialang-ui-core/-/concordialang-ui-core-0.2.0.tgz", - "integrity": "sha512-ws0Gyi9SYgV5QififA5I4XlcBIzwBnix27N16YfwZhFMHLnsKTXRf+Du7fuu3yEIpbnlNDCSRYwLj41dPKgugA==", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/concordialang-ui-core/-/concordialang-ui-core-0.2.3.tgz", + "integrity": "sha512-F++5OA0XzPOEO5p2kgiJUvmvncR56x+fWuZSoVKm+Fdt8w6yJwJ31ivCKqYrM6MPrQxE9nG816fzy4V1IiDeEw==", "requires": { "database-js": "^3.0.8", "database-js-json": "^1.2.1" } }, - "condense-newlines": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/condense-newlines/-/condense-newlines-0.2.1.tgz", - "integrity": "sha1-PemFVTE5R10yUCyDsC9gaE0kxV8=", - "requires": { - "extend-shallow": "^2.0.1", - "is-whitespace": "^0.3.0", - "kind-of": "^3.0.2" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "config-chain": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz", - "integrity": "sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==", - "requires": { - "ini": "^1.3.4", - "proto-list": "~1.2.1" - } - }, "content-type": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", @@ -1930,14 +1897,13 @@ "dev": true }, "cosmiconfig": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.0.tgz", - "integrity": "sha512-nxt+Nfc3JAqf4WIWd0jXLjTJZmsPLrA9DDc4nRw2KFJQJK7DNooqSXrNI7tzLG50CF8axczly5UV929tBmh/7g==", - "dev": true, + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", "requires": { "import-fresh": "^2.0.0", "is-directory": "^0.3.1", - "js-yaml": "^3.13.0", + "js-yaml": "^3.13.1", "parse-json": "^4.0.0" } }, @@ -2003,9 +1969,9 @@ } }, "database-js": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/database-js/-/database-js-3.0.8.tgz", - "integrity": "sha512-G2RZEXMK+DuIvviYs02xMViJ4gtc+VIXokoE+f2R4uyHXI3odDYSY3zqPoteFPnHzVDsnnrx336/yjOdXkl6Vg==" + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/database-js/-/database-js-3.0.9.tgz", + "integrity": "sha512-khaHbjOFLQLWSPm65Ix3G86iAXTLGof1JUQUDY/knuR0kBmN9Z8Lb7HXAl8FD9bBpIPRJ9Bh2dPKcWv7p3AwJw==" }, "database-js-json": { "version": "1.2.1", @@ -2226,24 +2192,6 @@ "safer-buffer": "^2.1.0" } }, - "editorconfig": { - "version": "0.15.3", - "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.3.tgz", - "integrity": "sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g==", - "requires": { - "commander": "^2.19.0", - "lru-cache": "^4.1.5", - "semver": "^5.6.0", - "sigmund": "^1.0.1" - }, - "dependencies": { - "commander": { - "version": "2.20.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", - "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==" - } - } - }, "elegant-spinner": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-1.0.1.tgz", @@ -2269,7 +2217,6 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, "requires": { "is-arrayish": "^0.2.1" } @@ -2341,8 +2288,7 @@ "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" }, "estraverse": { "version": "4.2.0", @@ -2719,7 +2665,8 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true }, "fsevents": { "version": "1.2.7", @@ -2741,7 +2688,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -2762,12 +2710,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2782,17 +2732,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -2909,7 +2862,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -2921,6 +2875,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -2935,6 +2890,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -2942,12 +2898,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -2966,6 +2924,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -3046,7 +3005,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -3058,6 +3018,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -3143,7 +3104,8 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -3179,6 +3141,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -3198,6 +3161,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -3241,12 +3205,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, @@ -3327,6 +3293,7 @@ "version": "7.1.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -3727,7 +3694,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", - "dev": true, "requires": { "caller-path": "^2.0.0", "resolve-from": "^3.0.0" @@ -3812,6 +3778,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -3820,12 +3787,8 @@ "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true }, "invariant": { "version": "2.2.4", @@ -3871,13 +3834,13 @@ "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" }, "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true }, "is-callable": { "version": "1.1.4", @@ -3942,13 +3905,13 @@ "is-directory": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", - "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", - "dev": true + "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=" }, "is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true }, "is-extglob": { "version": "2.1.1", @@ -4098,11 +4061,6 @@ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", "dev": true }, - "is-whitespace": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/is-whitespace/-/is-whitespace-0.3.0.tgz", - "integrity": "sha1-Fjnssb4DauxppUy7QBz77XEUq38=" - }, "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", @@ -4729,18 +4687,6 @@ "resolved": "https://registry.npmjs.org/jl-sql-api/-/jl-sql-api-2.8.1.tgz", "integrity": "sha1-BbB9JdxxhxcZwG/CFuS4lsZ4gUc=" }, - "js-beautify": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.9.1.tgz", - "integrity": "sha512-oxxvVZdOdUfzk8IOLBF2XUZvl2GoBEfA+b0of4u2EBY/46NlXasi8JdFvazA5lCrf9/lQhTjyVy2QCUW7iq0MQ==", - "requires": { - "config-chain": "^1.1.12", - "editorconfig": "^0.15.2", - "glob": "^7.1.3", - "mkdirp": "~0.5.0", - "nopt": "~4.0.1" - } - }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -4751,7 +4697,6 @@ "version": "3.13.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, "requires": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -4806,8 +4751,7 @@ "json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" }, "json-schema": { "version": "0.2.3", @@ -5140,8 +5084,7 @@ "lodash": { "version": "4.17.11", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==", - "dev": true + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" }, "lodash._reinterpolate": { "version": "3.0.0", @@ -5236,15 +5179,6 @@ "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", "dev": true }, - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, "make-dir": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", @@ -5384,6 +5318,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -5391,7 +5326,8 @@ "minimist": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true }, "mixin-deep": { "version": "1.3.1", @@ -5418,6 +5354,7 @@ "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, "requires": { "minimist": "0.0.8" } @@ -5511,15 +5448,6 @@ "which": "^1.3.0" } }, - "nopt": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", - "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, "normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -5663,6 +5591,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, "requires": { "wrappy": "1" } @@ -5716,11 +5645,6 @@ } } }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" - }, "os-locale": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", @@ -5771,16 +5695,8 @@ "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" - }, - "osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true }, "p-defer": { "version": "1.0.0", @@ -5858,7 +5774,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, "requires": { "error-ex": "^1.3.1", "json-parse-better-errors": "^1.0.1" @@ -5901,7 +5816,8 @@ "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true }, "path-is-inside": { "version": "1.0.2", @@ -6008,26 +5924,6 @@ "integrity": "sha512-sXe5lSt2WQlCbydGETgfm1YBShgOX4HxQkFPvbxkcwgDvGDeqVau8h+12+lmSVlP3rHPz0oavfddSZg/q+Szjw==", "dev": true }, - "pretty": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pretty/-/pretty-2.0.0.tgz", - "integrity": "sha1-rbx5YLe7/iiaVX3F9zdhmiINBqU=", - "requires": { - "condense-newlines": "^0.2.1", - "extend-shallow": "^2.0.1", - "js-beautify": "^1.6.12" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, "pretty-format": { "version": "24.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.7.0.tgz", @@ -6062,16 +5958,6 @@ "integrity": "sha512-CGuc0VUTGthpJXL36ydB6jnbyOf/rAHFvmVrJlH+Rg0DqqLFQGAP6hIaxD/G0OAmBJPhXDHuEJigrp0e0wFV6g==", "dev": true }, - "proto-list": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", - "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=" - }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" - }, "psl": { "version": "1.1.31", "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", @@ -6407,8 +6293,7 @@ "resolve-from": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", - "dev": true + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=" }, "resolve-url": { "version": "0.2.1", @@ -6609,11 +6494,6 @@ "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", "dev": true }, - "sigmund": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", - "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=" - }, "signal-exit": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", @@ -6865,8 +6745,7 @@ "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" }, "sshpk": { "version": "1.16.1", @@ -7699,7 +7578,8 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true }, "write-file-atomic": { "version": "2.4.2", @@ -7753,11 +7633,6 @@ "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", "dev": true }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" - }, "yargs": { "version": "12.0.5", "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", diff --git a/package.json b/package.json index 8f19a72..4078409 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ }, "husky": { "hooks": { - "pre-commit": "lint-staged" + "pre-commit": "npm run build && lint-staged" } }, "lint-staged": { @@ -72,8 +72,11 @@ "dependencies": { "@oclif/command": "^1.5.12", "@oclif/config": "^1.12.12", - "concordialang-ui-core": "^0.2.0", - "pretty": "^2.0.0", + "@types/lodash": "^4.14.134", + "case": "^1.6.1", + "concordialang-ui-core": "^0.2.3", + "cosmiconfig": "^5.2.1", + "lodash": "^4.17.11", "tslib": "^1.9.3" }, "devDependencies": { diff --git a/src/commands/generate.ts b/src/commands/generate.ts index 374d41a..51468d9 100644 --- a/src/commands/generate.ts +++ b/src/commands/generate.ts @@ -2,7 +2,7 @@ import {Command, flags} from '@oclif/command' import {ProcessResult} from 'concordialang-ui-core' import * as fs from 'fs' -import Generator from '../generator' +import HtmlUIPrototyper from '../html-ui-prototyper' export default class Generate extends Command { @@ -19,9 +19,9 @@ export default class Generate extends Command { if (flags.features) { const processResult: ProcessResult = JSON.parse(flags.features) as ProcessResult - const generator = new Generator(fs, flags.outputDir) + const generator = new HtmlUIPrototyper(fs, flags.outputDir) const result = await generator.generate(processResult.features) - this.log(JSON.stringify(result)) + this.log(result.join('\n')) } } } diff --git a/src/generator.ts b/src/html-ui-prototyper.ts similarity index 50% rename from src/generator.ts rename to src/html-ui-prototyper.ts index 075c50b..77f443a 100644 --- a/src/generator.ts +++ b/src/html-ui-prototyper.ts @@ -2,17 +2,23 @@ import { Feature, Prototyper, Widget } from 'concordialang-ui-core' import * as fs from 'fs' import { promisify } from 'util' import { format } from 'path' -const pretty = require('pretty') +const prettier = require('prettier') +const cosmiconfig = require('cosmiconfig') import WidgetFactory from './widgets/widget-factory' +import { AppConfig } from './interfaces/app-config' -export default class Generator implements Prototyper { +export default class HtmlUIPrototyper implements Prototyper { constructor(private _fs: any = fs, private _outputDir: string) { } public async generate(features: Feature[]): Promise { - const factory = new WidgetFactory() - let createFilePromises: Promise[] = [] + const appConfig: AppConfig = this.getAppConfig() + const factory = new WidgetFactory(appConfig) + + if (features.length === 0) return Promise.resolve([ 'No features found' ]) + + const createFilePromises: Promise[] = [] for (let feature of features) { const elements: Widget[] = feature.uiElements.map(uiElement => (factory.create(uiElement))) @@ -24,13 +30,24 @@ export default class Generator implements Prototyper { private async createHtmlFile(fileName: string, widgets: Widget[]): Promise { let content = widgets.reduce((result, widget) => { - return result + widget.renderToString() + '\n' + return result + widget.renderToString() }, '') - content = pretty(`
\n${content}
`, {ocd: true}) + content = prettier.format(`
${content}
`, {parser: 'html', htmlWhitespaceSensitivity: 'ignore'}) const path = format({ dir: this._outputDir, name: fileName, ext: '.html' }) await promisify(fs.writeFile)(path, content) return path } + + private getAppConfig(): AppConfig { + try { + const explorer = cosmiconfig() + const configFile = explorer.loadSync('concordialang-ui-html.json') + const appConfig: AppConfig = configFile.config + return appConfig + } catch (e) { + throw new Error('Config file not found') + } + } } diff --git a/src/index.ts b/src/index.ts index 95bc5ff..48bbdaa 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1 +1 @@ -export * from './generator'; +export * from './html-ui-prototyper'; diff --git a/src/interfaces/app-config.ts b/src/interfaces/app-config.ts new file mode 100644 index 0000000..b462f6f --- /dev/null +++ b/src/interfaces/app-config.ts @@ -0,0 +1,24 @@ +export interface AppConfig { + widgets?: { + input?: WidgetConfig, + radio?: WidgetConfig, + checkbox?: WidgetConfig, + select?: WidgetConfig, + label?: LabelConfig + } +} + +export interface WidgetConfig { + opening: string, + closure?: string, + optionOpening?: string, + optionClosure?: string, + wrapperOpening?: string, + wrapperClosure?: string, + label?: LabelConfig +} + +interface LabelConfig { + opening: string, + closure: string +} diff --git a/src/utils/prop.ts b/src/utils/prop.ts new file mode 100644 index 0000000..15d322f --- /dev/null +++ b/src/utils/prop.ts @@ -0,0 +1,25 @@ +import { camel } from 'case' + +export const PROPS_INJECTION_POINT = '%s' + +export function formatProperties(props: any, validProperties: string[]): string { + const translateProp = (key: string) => { + switch(key) { + case 'format': return 'pattern'; + default: return key; + } + } + + const getFormattedProp = (key: string) => { + let value = camel(props[key].toString()) + return `${translateProp(key)}="${value}"` + } + + const formatValid = (result: string, prop: string) => { + return validProperties.includes(prop) + ? result + getFormattedProp(prop) + ' ' + : result + } + + return Object.keys(props).reduce(formatValid, '').trimRight() +} diff --git a/src/widgets/button.ts b/src/widgets/button.ts index c00691a..8a93ff9 100644 --- a/src/widgets/button.ts +++ b/src/widgets/button.ts @@ -1,22 +1,24 @@ -import {Widget} from 'concordialang-ui-core' +import { Widget } from 'concordialang-ui-core' +import { WidgetConfig } from '../interfaces/app-config' +import { formatProperties, PROPS_INJECTION_POINT } from '../utils/prop' -import {formatProperties} from './prop' +export default class Button extends Widget { + private readonly VALID_PROPERTIES = ['id', 'disabled', 'value'] -export class Button extends Widget { - private readonly VALID_PROPERTIES = ['id', 'disabled', 'value'] + constructor(props: any, name: string, private _config: WidgetConfig) { + super(props, name || '') + } - constructor(props: any, name?: string) { - super(props, name || '') - } + public renderToString(): string { + const buttonType = this.getType(this.props.datatype as string) + let properties = formatProperties(this.props, this.VALID_PROPERTIES) + properties = `${ buttonType } ${ properties }` + const buttonOpening = this._config.opening.replace(PROPS_INJECTION_POINT, properties) + const buttonClosure = this._config.closure + return buttonOpening + this.name + buttonClosure + } - public renderToString(): string { - // const inputType = this.getType(this.props.datatype as string) - const properties = formatProperties(this.props, this.VALID_PROPERTIES) - // return `` - return `` - } - - private getType(datatype: string): string { - return `type="${datatype || 'button'}"` - } + private getType(datatype: string): string { + return `type="${datatype || 'button'}"` + } } diff --git a/src/widgets/checkbox.ts b/src/widgets/checkbox.ts index 1007fa1..4c78bca 100644 --- a/src/widgets/checkbox.ts +++ b/src/widgets/checkbox.ts @@ -1,17 +1,21 @@ -import {Widget} from 'concordialang-ui-core' +import { Widget } from 'concordialang-ui-core' +import { WidgetConfig } from '../interfaces/app-config' +import { formatProperties, PROPS_INJECTION_POINT } from '../utils/prop' +import { wrap } from './wrapper' -import {formatProperties} from './prop' - -export class Checkbox extends Widget { +export default class Checkbox extends Widget { private readonly VALID_PROPERTIES = ['value', 'required'] - constructor(props: any, name: string) { + constructor(props: any, name: string, private _config: WidgetConfig) { super(props, name) } public renderToString(): string { - const properties = formatProperties(this.props, this.VALID_PROPERTIES) - if (properties) return `
\n${this.name}\n
` - return `
\n${this.name}\n
` + const inputType = 'type="checkbox"' + let properties = formatProperties(this.props, this.VALID_PROPERTIES) + properties = `${inputType} ${properties}` + const inputOpening = this._config.opening.replace(PROPS_INJECTION_POINT, properties) + const inputClosure = this._config.closure || '' + return wrap(inputOpening + this.name + inputClosure, this._config) } } diff --git a/src/widgets/index.ts b/src/widgets/index.ts deleted file mode 100644 index 7fe5a18..0000000 --- a/src/widgets/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export * from './button'; -export * from './input'; -export * from './prop'; -export * from './widget-factory'; diff --git a/src/widgets/input.ts b/src/widgets/input.ts index 87953a5..adb938e 100644 --- a/src/widgets/input.ts +++ b/src/widgets/input.ts @@ -1,6 +1,10 @@ -import {Widget} from 'concordialang-ui-core' +import { Widget } from 'concordialang-ui-core' +import { get } from 'lodash'; -import {formatProperties, createLabel} from './prop' +import { WidgetConfig } from '../interfaces/app-config' +import { formatProperties, PROPS_INJECTION_POINT } from '../utils/prop' +import { createLabel } from './label' +import { wrap } from './wrapper' const enum DataTypes { STRING = 'string', @@ -11,33 +15,33 @@ const enum DataTypes { DATETIME = 'datetime' } -export class Input extends Widget { +export default class Input extends Widget { private readonly VALID_PROPERTIES = ['id', 'editable', 'minlength', 'maxlength', 'required', 'format'] - constructor(props: any, name: string) { + constructor(props: any, name: string, private _config: WidgetConfig) { super(props, name) } public renderToString(): string { const inputType = this.getType(this.props.datatype as string) const properties = formatProperties(this.props, this.VALID_PROPERTIES) - const input = properties ? `\n` : `\n` - const label = createLabel(this.name, this.props.id.toString()) - return `
\n${label + input}
` + const inputOpening = this._config.opening.replace(PROPS_INJECTION_POINT, `${ inputType } ${ properties }`) + const inputClosure = this._config.closure || '' + const label = createLabel(this.name, this.props.id.toString(), this._config) + return wrap(label + inputOpening + inputClosure, this._config) } private getType(datatype: string): string { - const typeProperty = this.typeForDataType(datatype) - return `type="${typeProperty}"` - } + let typeProperty - private typeForDataType(datatype: string): string { switch (datatype) { case DataTypes.INTEGER: - case DataTypes.DOUBLE: return 'number' - case DataTypes.TIME: return 'time' - case DataTypes.DATETIME: return 'datetime-local' + case DataTypes.DOUBLE: typeProperty = 'number'; break + case DataTypes.TIME: typeProperty = 'time'; break + case DataTypes.DATETIME: typeProperty = 'datetime-local'; break + default: typeProperty = 'text' } - return 'text' + + return `type="${typeProperty}"` } } diff --git a/src/widgets/label.ts b/src/widgets/label.ts new file mode 100644 index 0000000..a75a5e2 --- /dev/null +++ b/src/widgets/label.ts @@ -0,0 +1,13 @@ +import { WidgetConfig } from '../interfaces/app-config' +import { PROPS_INJECTION_POINT } from '../utils/prop' + +export function createLabel(widgetName: string, widgetId: string, widgetConfig: WidgetConfig): string { + if (!widgetConfig.label) return '' + + const idPattern = /^(#|~|\d|\w).*/ + const labelFor = widgetId.match(idPattern) ? `for="${widgetId.replace(/^#|~/ , '')}"` : '' + const labelOpening = widgetConfig.label.opening.replace(PROPS_INJECTION_POINT, labelFor) + const labelClosure = widgetConfig.label.closure + + return labelOpening + widgetName + labelClosure +} diff --git a/src/widgets/prop.ts b/src/widgets/prop.ts deleted file mode 100644 index 0af134d..0000000 --- a/src/widgets/prop.ts +++ /dev/null @@ -1,49 +0,0 @@ -export function formatProperties(props: any, validProperties: string[]): string { - const translateProp = (key: string) => { - switch(key) { - case 'format': return 'pattern'; - default: return key; - } - } - - const getFormattedProp = (key: string) => { - let value = props[key] - const invalidIdPattern = /^\/\// - - if(key === 'id') { - let newKey = key - if(!invalidIdPattern.test(value)) { - const validIdPattern = /^#|~/ - const validClassPattern = /^\./ - - if(validIdPattern.test(value)) { - value = value.toString().replace(validIdPattern, '') - } else if(validClassPattern.test(value)) { - newKey = 'class' - value = value.toString().replace(validClassPattern, '') - } - return `${translateProp(newKey)}="${value}"` - } - - } - - - return `${translateProp(key)}="${value}"` - } - - const formatValid = (result: string, prop: string) => { - return validProperties.includes(prop) - ? result + getFormattedProp(prop) + ' ' - : result - } - - return Object.keys(props).reduce(formatValid, '').trimRight() -} - -export function createLabel(name: string, id: string): string { - - const validIdPattern = /^(#|~|\d|\w).*/ - - const labelFor = (validIdPattern.test(id)) ? `for="${id.replace(/^#|~/ , '')}"` : '' - return `\n` -} \ No newline at end of file diff --git a/src/widgets/radio.ts b/src/widgets/radio.ts index 73b2117..0044ec3 100644 --- a/src/widgets/radio.ts +++ b/src/widgets/radio.ts @@ -1,28 +1,37 @@ -import {Widget} from 'concordialang-ui-core' +import { Widget } from 'concordialang-ui-core' +import { WidgetConfig } from '../interfaces/app-config' +import { formatProperties, PROPS_INJECTION_POINT } from '../utils/prop' +import { createLabel } from './label' +import { wrap } from './wrapper' -import {formatProperties, createLabel} from './prop' +export default class Radio extends Widget { + private readonly VALID_PROPERTIES = [ 'value' ] -export class Radio extends Widget { - private readonly VALID_PROPERTIES = ['value'] - - constructor(props: any, name: string) { + constructor(props: any, name: string, private _config: WidgetConfig) { super(props, name) } public renderToString(): string { - const properties = formatProperties(this.props, this.VALID_PROPERTIES) + const inputType = 'type="radio"' + const label = createLabel(this.name, '', this._config) let inputs: String[] = [] - const label = createLabel(this.name, this.props.id.toString()) - const inputName = this.name.toLowerCase() - if (properties) { - for (let value of this.props.value as Array) { - let input = `${value}` - inputs.push(input) - } - return `
\n${label + inputs.join('\n')}\n
` + for (let value of this.props.value as Array) { + // TODO: o que fazer no formatProperties em relação ao value? + // provavelmente terei que instalar o pacote "case" + // para ter 'value="algumaCoisa"', quando value for "Alguma Coisa" + // + // TODO: adicionar propriedades 'id' e 'nome' + + const props = Object.assign({}, this.props, { value }) + let properties = formatProperties(props, this.VALID_PROPERTIES) + properties = `${inputType} ${properties}` + const inputOpening = this._config.opening.replace(PROPS_INJECTION_POINT, properties) + const inputClosure = this._config.closure || '' + inputs.push(inputOpening + value + inputClosure) } - return '
\n\n
' + + return wrap(label + inputs.join(''), this._config) } } diff --git a/src/widgets/select.ts b/src/widgets/select.ts index 18ab9a9..856a86f 100644 --- a/src/widgets/select.ts +++ b/src/widgets/select.ts @@ -1,29 +1,38 @@ -import {Widget} from 'concordialang-ui-core' +import { Widget } from 'concordialang-ui-core' +import { WidgetConfig } from '../interfaces/app-config' +import { formatProperties, PROPS_INJECTION_POINT } from '../utils/prop' +import { createLabel } from './label' +import { wrap } from './wrapper' -import {formatProperties, createLabel} from './prop' +export default class Select extends Widget { + private readonly SELECT_VALID_PROPERTIES = ['id', 'required'] + private readonly OPTION_VALID_PROPERTIES = ['value'] -export class Select extends Widget { - private readonly VALID_PROPERTIES = ['id', 'required'] - - constructor(props: any, name: string) { + constructor(props: any, name: string, private _config: WidgetConfig) { super(props, name) } public renderToString(): string { - const properties = formatProperties(this.props, this.VALID_PROPERTIES) - if (!properties) return '
\n\n
' + const properties = formatProperties(this.props, this.SELECT_VALID_PROPERTIES) + const selectOpening = this._config.opening.replace(PROPS_INJECTION_POINT, properties) + const selectClosure = this._config.closure const options = this.getOptions() - const select = `\n` - const label = createLabel(this.name, this.props.id.toString()) - return `
\n${label + select}
` + const select = selectOpening + options + selectClosure + const label = createLabel(this.name, this.props.id.toString(), this._config) + return wrap(label + select, this._config) } private getOptions(): string { + if (!this._config.optionOpening) return '' + let options: string[] = [] for (let value of this.props.value as Array) { - let option = `` - options.push(option) + const optionProps = { value } + const properties = formatProperties(optionProps, this.OPTION_VALID_PROPERTIES) + const optionOpening = this._config.optionOpening.replace(PROPS_INJECTION_POINT, properties) + const optionClosure = this._config.optionClosure + options.push(optionOpening + value + optionClosure) } - return options.join('\n') + return options.join('') } } diff --git a/src/widgets/widget-factory.ts b/src/widgets/widget-factory.ts index c7954ef..e6dca4c 100644 --- a/src/widgets/widget-factory.ts +++ b/src/widgets/widget-factory.ts @@ -1,10 +1,12 @@ import {UiElement, Widget} from 'concordialang-ui-core' +import { get } from 'lodash'; -import {Button} from './button' -import {Input} from './input' -import {Checkbox} from './checkbox' -import {Radio} from './radio' -import {Select} from './select' +import Button from './button' +import Input from './input' +import Checkbox from './checkbox' +import Radio from './radio' +import Select from './select' +import { AppConfig, WidgetConfig } from '../interfaces/app-config' const enum Widgets { BUTTON = 'button', @@ -15,14 +17,45 @@ const enum Widgets { } export default class WidgetFactory { + constructor(private _config: AppConfig) {} + create(element: UiElement): Widget { switch (element.widget) { - case Widgets.TEXTBOX: return new Input(element.props, element.name) - case Widgets.BUTTON: return new Button(element.props, element.name) - case Widgets.CHECKBOX: return new Checkbox(element.props, element.name) - case Widgets.RADIO: return new Radio(element.props, element.name) - case Widgets.SELECT: return new Select(element.props, element.name) + case Widgets.TEXTBOX: return this.createInputElement(element) + case Widgets.BUTTON: return this.createButtonElement(element) + case Widgets.CHECKBOX: return this.createCheckboxElement(element) + case Widgets.RADIO: return this.createRadioElement(element) + case Widgets.SELECT: return this.createSelectElement(element) default: throw new Error(`Invalid widget type: ${element.widget}`) } } + + private createInputElement(element: UiElement): Input { + const widgetConfig: WidgetConfig = get(this._config, 'widgets.input') + widgetConfig.label = widgetConfig.label || get(this._config, 'widgets.label') + return new Input(element.props, element.name, widgetConfig) + } + + private createRadioElement(element: UiElement): Radio { + const widgetConfig: WidgetConfig = get(this._config, 'widgets.radio') + widgetConfig.label = widgetConfig.label || get(this._config, 'widgets.label') + return new Radio(element.props, element.name, widgetConfig) + } + + private createCheckboxElement(element: UiElement): Checkbox { + const widgetConfig: WidgetConfig = get(this._config, 'widgets.checkbox') + widgetConfig.label = widgetConfig.label || get(this._config, 'widgets.label') + return new Checkbox(element.props, element.name, widgetConfig) + } + + private createSelectElement(element: UiElement): Select { + const widgetConfig: WidgetConfig = get(this._config, 'widgets.select') + widgetConfig.label = widgetConfig.label || get(this._config, 'widgets.label') + return new Select(element.props, element.name, widgetConfig) + } + + private createButtonElement(element: UiElement): Button { + const widgetConfig: WidgetConfig = get(this._config, 'widgets.button') + return new Button(element.props, element.name, widgetConfig) + } } diff --git a/src/widgets/wrapper.ts b/src/widgets/wrapper.ts new file mode 100644 index 0000000..5925db1 --- /dev/null +++ b/src/widgets/wrapper.ts @@ -0,0 +1,7 @@ +import { WidgetConfig } from '../interfaces/app-config' + +export function wrap(elements: string, widgetConfig: WidgetConfig): string { + if (widgetConfig.wrapperOpening && widgetConfig.wrapperClosure) + return widgetConfig.wrapperOpening + elements + widgetConfig.wrapperClosure + return elements +} diff --git a/test/commands/generate.spec.ts b/test/commands/generate.spec.ts index fd7d496..748335d 100644 --- a/test/commands/generate.spec.ts +++ b/test/commands/generate.spec.ts @@ -1,12 +1,31 @@ +import { fs, vol } from 'memfs' +const cosmiconfig = require('cosmiconfig') + import Generate from '../../src/commands/generate' +jest.mock('cosmiconfig') + describe('Generate', () => { + const CURRENT_DIR: string = process.cwd() + + beforeEach(() => { + vol.fromJSON({ + './concordialang-ui-html.json': '{}' + }, CURRENT_DIR) + + const explorer = { + loadSync: () => ({ + config: vol.readFileSync(`${ CURRENT_DIR }/concordialang-ui-html.json`, 'utf8') + }) + } + cosmiconfig.mockReturnValue(explorer) + }) + it('should print a JSON content', async () => { let spy = jest.spyOn(process.stdout, 'write'); - await Generate.run([]) // TODO: pass a parameter - // expect(spy).toBeCalledWith({}) - expect(spy).not.toBeCalled() // TODO: change this assertion + await Generate.run(['--features', '{ "features": [] }', '--outputDir', 'outputDir']) + expect(spy).toBeCalledWith("No features found\n") }) -}) \ No newline at end of file +}) diff --git a/test/generator.spec.ts b/test/generator.spec.ts deleted file mode 100644 index d574429..0000000 --- a/test/generator.spec.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { Feature } from 'concordialang-ui-core' -import { minify } from 'html-minifier' -import { fs, vol } from 'memfs' -import { promisify } from 'util' - -import Generator from '../src/generator' - -describe('Generator', () => { - - const CURRENT_DIR: string = process.cwd() - - let generator: Generator | null - - beforeEach(() => { - vol.mkdirpSync(CURRENT_DIR) // Synchronize with the current fs structure - generator = new Generator(fs) // In-memory fs - }) - - afterEach(() => { - vol.reset() // Erase in-memory structure - generator = null - }) - - async function expectFeaturesToProduceHtml(features: Feature[], htmls: string[]): Promise { - if (! generator) { - generator = new Generator(fs) - } - const files: string[] = await generator.generate(features) - expect(files).toHaveLength(htmls.length) - // tslint:disable-next-line:forin - for (let i in files) { - await expectFileToHaveHtml(files[i], htmls[i]) - } - } - - async function expectFileToHaveHtml(filePath: string, html: string): Promise { - const expected: string = minify(html) - const readF = promisify(fs.readFile) - const content: string = await readF(filePath) - const produced: string = minify(content) - expect(produced).toEqual(expected) - } - - it('produces an HTML file from features', async () => { - const features: Feature[] = [ /* something here */ ] - const htmls: string[] = [ /* put the expected html here */]; - await expectFeaturesToProduceHtml(features, htmls) - }) - -}) diff --git a/test/html-ui-prototyper.spec.ts b/test/html-ui-prototyper.spec.ts new file mode 100644 index 0000000..698ae9a --- /dev/null +++ b/test/html-ui-prototyper.spec.ts @@ -0,0 +1,70 @@ +import { Feature } from 'concordialang-ui-core' +import { minify } from 'html-minifier' +import { fs, vol } from 'memfs' +import { promisify } from 'util' +const cosmiconfig = require('cosmiconfig') + +import HtmlUIPrototyper from '../src/html-ui-prototyper' + +jest.mock('cosmiconfig') + +describe('HtmlUIPrototyper', () => { + + const CURRENT_DIR: string = process.cwd() + let prototyper: HtmlUIPrototyper | null + const appConfig = { + widgets: { + input: {}, + label: {} + } + } + + beforeEach(() => { + vol.fromJSON({ + './concordialang-ui-html.json': JSON.stringify(appConfig) + }, CURRENT_DIR) + + const explorer = { + loadSync: () => ({ + config: vol.readFileSync(`${ CURRENT_DIR }/concordialang-ui-html.json`, 'utf8') + }) + } + cosmiconfig.mockReturnValue(explorer) + + prototyper = new HtmlUIPrototyper(fs, CURRENT_DIR) // In-memory fs + }) + + afterEach(() => { + vol.reset() // Erase in-memory structure + prototyper = null + }) + + async function expectFeaturesToProduceHtml(features: Feature[], htmls: string[]): Promise { + if (! prototyper) { + prototyper = new HtmlUIPrototyper(fs, CURRENT_DIR) + } + const files: string[] = await prototyper.generate(features) + expect(files).toHaveLength(htmls.length) + // tslint:disable-next-line:forin + for (let i in files) { + await expectFileToHaveHtml(files[i], htmls[i]) + } + } + + async function expectFileToHaveHtml(filePath: string, html: string): Promise { + const expected: string = minify(html) + const readF = promisify(fs.readFile) + const content: string = await readF(filePath) + const produced: string = minify(content) + expect(produced).toEqual(expected) + } + + // FIXME the content of app config file is a string. It should be an object. + // Maybe we will have to mock loadJson from cosmiconfig. + xit('produces an HTML file from features', async () => { + const features: Feature[] = [ { name: 'Test Feature', uiElements: [ { name: 'Name', widget: 'textbox', props: { id: 'name' }, position: 0 } ], position: 0 } ] + const htmls: string[] = [ /* put the expected html here */]; + await expectFeaturesToProduceHtml(features, htmls) + }) + +}) diff --git a/test/utils/format-properties.spec.ts b/test/utils/format-properties.spec.ts new file mode 100644 index 0000000..17bc7ae --- /dev/null +++ b/test/utils/format-properties.spec.ts @@ -0,0 +1,18 @@ +import { formatProperties } from '../../src/utils/prop' + +describe('formatProperties', () => { + describe('when there is an invalid property', () => { + const props = { + id: 'id', + name: 'name', + required: true, + foo: 'bar' + } + + const validProperties = ['id', 'name', 'required'] + + it('produces a string with the valid properties only', () => { + expect(formatProperties(props, validProperties)).toEqual('id="id" name="name" required="true"') + }) + }) +}) diff --git a/test/widgets/button.spec.ts b/test/widgets/button.spec.ts index d3c0d99..c46a90f 100644 --- a/test/widgets/button.spec.ts +++ b/test/widgets/button.spec.ts @@ -1,20 +1,29 @@ import { UiElement} from 'concordialang-ui-core' -import { Button } from '../../src/widgets/button' +import { AppConfig, WidgetConfig } from '../../src/interfaces/app-config' +import Button from '../../src/widgets/button' describe('Button', () => { describe('renderToString', () => { - it('without properties', () => { - const b = new Button({}) - expect(b.renderToString()).toBe('') - }) + const uiElement: UiElement = { + name: 'Save', + widget: 'button', + position: 7, + props: { + id: 'save' + } + } + + const widgetConfig: WidgetConfig = { + opening: '' + } it('produces html from a button element', async () => { - const buttonUiElement: UiElement = { name: 'OK', widget: 'button', position: 30, props: {} } - const buttonWidget: Button = new Button(buttonUiElement.props, buttonUiElement.name) + const buttonWidget: Button = new Button(uiElement.props, uiElement.name, widgetConfig) const result = buttonWidget.renderToString() - expect(result).toEqual(``) + expect(result).toEqual(``) }) }) }) diff --git a/test/widgets/checkbox.spec.ts b/test/widgets/checkbox.spec.ts index 84e1b1b..5bea6c6 100644 --- a/test/widgets/checkbox.spec.ts +++ b/test/widgets/checkbox.spec.ts @@ -1,10 +1,11 @@ import { UiElement } from 'concordialang-ui-core' -import { Checkbox } from '../../src/widgets/checkbox' +import { AppConfig, WidgetConfig } from '../../src/interfaces/app-config' +import Checkbox from '../../src/widgets/checkbox' describe('Checkbox', () => { describe('renderToString', () => { - const defaultProps: UiElement = { + const uiElement: UiElement = { name: 'Web Developer', widget: 'checkbox', position: 16, @@ -13,33 +14,26 @@ describe('Checkbox', () => { } } - const subject = (uiElement?: UiElement) => ( - uiElement ? - new Checkbox(uiElement.props, uiElement.name) : - new Checkbox({}) - ) + const widgetConfig: WidgetConfig = { + opening: '', + wrapperOpening: '
', + wrapperClosure: '
', + label: { + opening: '' + } + } - it('without properties', () => { - const inputWidget: Checkbox = subject() - expect(inputWidget.renderToString()).toEqual(expect.stringContaining('')) + it('produces html from an input element with name', async () => { + const inputWidget: Checkbox = new Checkbox(uiElement.props, uiElement.name, widgetConfig) + const result = inputWidget.renderToString() + expect(result).toEqual(expect.stringContaining('Web Developer')) }) - it('surrounds the input with a div', () => { - const inputWidget: Checkbox = subject() + it('produces a wrapper for the input element', () => { + const inputWidget: Checkbox = new Checkbox(uiElement.props, uiElement.name, widgetConfig) const result = inputWidget.renderToString() expect(result).toEqual(expect.stringMatching(/^
(.|\s)*<\/div>$/)) }) - - it('produces html from an input element with name', async () => { - const inputWidget: Checkbox = subject(defaultProps) - const result = inputWidget.renderToString() - expect(result).toEqual(expect.stringContaining('Web Developer')) - }) - - it('produces html from an input element without name', async () => { - const inputWidget: Checkbox = subject({ ...defaultProps, name: undefined }) - const result = inputWidget.renderToString() - expect(result).toEqual(expect.stringMatching('\n')) - }) }) }) diff --git a/test/widgets/input.spec.ts b/test/widgets/input.spec.ts index 8096c03..c68fb18 100644 --- a/test/widgets/input.spec.ts +++ b/test/widgets/input.spec.ts @@ -1,10 +1,10 @@ import { UiElement } from 'concordialang-ui-core' -import { Input } from '../../src/widgets/input' +import { AppConfig, WidgetConfig } from '../../src/interfaces/app-config' +import Input from '../../src/widgets/input' describe('Input', () => { - describe('renderToString', () => { - const defaultProps: UiElement = { + const uiElement: UiElement = { name: 'Username', widget: 'textbox', position: 16, @@ -16,39 +16,46 @@ describe('Input', () => { } } - const subject = (uiElement?: UiElement) => ( - uiElement ? - new Input(uiElement.props, uiElement.name) : - new Input({}) - ) - - it('without properties', () => { - const inputWidget: Input = subject() - expect(inputWidget.renderToString()).toEqual(expect.stringContaining('')) - }) + const widgetConfig: WidgetConfig = { + opening: '', + wrapperOpening: '
', + wrapperClosure: '
', + label: { + opening: '' + } + } it('produces html from an input element with name', async () => { - const inputWidget: Input = subject(defaultProps) - const result = inputWidget.renderToString() - expect(result).toEqual(expect.stringContaining('')) - }) - - it('produces html from an input element without name', async () => { - const inputWidget: Input = subject({ ...defaultProps, name: undefined }) + const inputWidget: Input = new Input(uiElement.props, uiElement.name, widgetConfig) const result = inputWidget.renderToString() - expect(result).toEqual(expect.stringContaining('')) + expect(result).toEqual(expect.stringContaining('')) }) it('produces a label for the input element', async () => { - const inputWidget: Input = subject(defaultProps) + const inputWidget: Input = new Input(uiElement.props, uiElement.name, widgetConfig) const result = inputWidget.renderToString() expect(result).toEqual(expect.stringContaining('')) }) - it('surrounds the input with a div', () => { - const inputWidget: Input = subject() + it('produces a wrapper for the input element', () => { + const inputWidget: Input = new Input(uiElement.props, uiElement.name, widgetConfig) const result = inputWidget.renderToString() expect(result).toEqual(expect.stringMatching(/^
(.|\s)*<\/div>$/)) }) + + describe('when the label is not defined', () => { + const widgetConfig: WidgetConfig = { + opening: '', + wrapperOpening: '
', + wrapperClosure: '
' + } + + it('does not produce a label for the input element', async () => { + const inputWidget: Input = new Input(uiElement.props, uiElement.name, widgetConfig) + const result = inputWidget.renderToString() + expect(result).not.toEqual(expect.stringContaining('label')) + }) + }) }) }) diff --git a/test/widgets/prop.spec.ts b/test/widgets/prop.spec.ts index fccdacd..d0e748a 100644 --- a/test/widgets/prop.spec.ts +++ b/test/widgets/prop.spec.ts @@ -1,4 +1,4 @@ -import { formatProperties } from '../../src/widgets/prop' +import { formatProperties } from '../../src/utils/prop' describe('formatProperties', () => { it('creates a string with the valid properties', () => { diff --git a/test/widgets/radio.spec.ts b/test/widgets/radio.spec.ts index fa9286e..1e5c73d 100644 --- a/test/widgets/radio.spec.ts +++ b/test/widgets/radio.spec.ts @@ -1,9 +1,10 @@ import { UiElement } from 'concordialang-ui-core' -import { Radio } from '../../src/widgets/radio' +import { AppConfig, WidgetConfig } from '../../src/interfaces/app-config' +import Radio from '../../src/widgets/radio' describe('Radio', () => { describe('renderToString', () => { - const defaultProps: UiElement = { + const uiElement: UiElement = { name: 'Gender', widget: 'radio', position: 7, @@ -13,34 +14,33 @@ describe('Radio', () => { } } - const subject = (uiElement?: UiElement) => ( - uiElement ? - new Radio(uiElement.props, uiElement.name) : - new Radio({}) - ) + const widgetConfig: WidgetConfig = { + opening: '', + wrapperOpening: '
', + wrapperClosure: '
', + label: { + opening: '' + } + } - it('without properties', () => { - const inputWidget: Radio = subject() - expect(inputWidget.renderToString()).toEqual(expect.stringContaining('')) + it('produces html from an radio element with name', async () => { + const inputWidget: Radio = new Radio(uiElement.props, uiElement.name, widgetConfig) + const result = inputWidget.renderToString() + expect(result).toEqual(expect.stringContaining('Male')) + expect(result).toEqual(expect.stringContaining('Female')) }) - it('surrounds the input with a div', () => { - const inputWidget: Radio = subject() - const result = inputWidget.renderToString() - expect(result).toEqual(expect.stringMatching(/^
(.|\s)*<\/div>$/)) - }) - - it('produces html from an input element with name', async () => { - const inputWidget: Radio = subject(defaultProps) + it('produces a label for the input element', async () => { + const inputWidget: Radio = new Radio(uiElement.props, uiElement.name, widgetConfig) const result = inputWidget.renderToString() - expect(result).toEqual(expect.stringContaining('Male')) - expect(result).toEqual(expect.stringContaining('Female')) - }) + expect(result).toEqual(expect.stringContaining('')) + }) - it('produces a label for the select element', async () => { - const inputWidget: Radio = subject(defaultProps) + it('produces a wrapper for the input element', () => { + const inputWidget: Radio = new Radio(uiElement.props, uiElement.name, widgetConfig) const result = inputWidget.renderToString() - expect(result).toEqual(expect.stringContaining('')) + expect(result).toEqual(expect.stringMatching(/^
(.|\s)*<\/div>$/)) }) }) }) diff --git a/test/widgets/select.spec.ts b/test/widgets/select.spec.ts index 637656c..eca3546 100644 --- a/test/widgets/select.spec.ts +++ b/test/widgets/select.spec.ts @@ -1,9 +1,10 @@ import { UiElement } from 'concordialang-ui-core' -import { Select } from '../../src/widgets/select' +import { AppConfig, WidgetConfig } from '../../src/interfaces/app-config' +import Select from '../../src/widgets/select' describe('Select', () => { describe('renderToString', () => { - const defaultProps: UiElement = { + const uiElement: UiElement = { name: 'Gender', widget: 'select', position: 7, @@ -13,41 +14,43 @@ describe('Select', () => { } } - const subject = (uiElement?: UiElement) => ( - uiElement ? - new Select(uiElement.props, uiElement.name) : - new Select({}) - ) - - it('without properties', () => { - const inputWidget: Select = subject() - expect(inputWidget.renderToString()).toEqual(expect.stringContaining('')) - }) - - it('surrounds the select with a div', () => { - const inputWidget: Select = subject() - const result = inputWidget.renderToString() - expect(result).toEqual(expect.stringMatching(/^
(.|\s)*<\/div>$/)) - }) + const widgetConfig: WidgetConfig = { + opening: '', + optionOpening: '', + wrapperOpening: '
', + wrapperClosure: '
', + label: { + opening: '' + } + } it('produces html from an select element with name', async () => { - const inputWidget: Select = subject(defaultProps) + const inputWidget: Select = new Select(uiElement.props, uiElement.name, widgetConfig) const result = inputWidget.renderToString() expect(result).toEqual(expect.stringContaining('')) }) + it('produces the options for the select element', async () => { + const inputWidget: Select = new Select(uiElement.props, uiElement.name, widgetConfig) + const result = inputWidget.renderToString() + expect(result).toEqual(expect.stringContaining('')) + expect(result).toEqual(expect.stringContaining('')) + }) + it('produces a label for the select element', async () => { - const inputWidget: Select = subject(defaultProps) + const inputWidget: Select = new Select(uiElement.props, uiElement.name, widgetConfig) const result = inputWidget.renderToString() expect(result).toEqual(expect.stringContaining('')) }) - it('produces the options for the select element', async () => { - const inputWidget: Select = subject(defaultProps) - const result = inputWidget.renderToString() - expect(result).toEqual(expect.stringContaining('')) - expect(result).toEqual(expect.stringContaining('')) + it('produces a wrapper for the input element', () => { + const inputWidget: Select = new Select(uiElement.props, uiElement.name, widgetConfig) + const result = inputWidget.renderToString() + expect(result).toEqual(expect.stringMatching(/^
(.|\s)*<\/div>$/)) }) }) }) diff --git a/test/widgets/widget-factory.spec.ts b/test/widgets/widget-factory.spec.ts index 9354c1a..74167ed 100644 --- a/test/widgets/widget-factory.spec.ts +++ b/test/widgets/widget-factory.spec.ts @@ -1,12 +1,25 @@ import { UiElement } from 'concordialang-ui-core' +import { AppConfig, WidgetConfig } from '../../src/interfaces/app-config' import WidgetFactory from '../../src/widgets/widget-factory' -import { Button } from '../../src/widgets/button'; -import { Input } from '../../src/widgets/input'; +import Button from '../../src/widgets/button'; +import Input from '../../src/widgets/input'; describe('WidgetFactory', () => { - let widgetFactory: WidgetFactory = new WidgetFactory() + const appConfig: AppConfig = { + widgets: { + input: { + opening: '', + label: { + opening: '' + } + } + } + } + + let widgetFactory: WidgetFactory = new WidgetFactory(appConfig) describe('create', () => { it('create button with valid properties', () => { @@ -18,10 +31,10 @@ describe('WidgetFactory', () => { } const buttonWidget = new Button(buttonUiElement.props, buttonUiElement.name) - + expect(widgetFactory.create(buttonUiElement)).toEqual(buttonWidget) }) - + it('create input with valid properties', async () => { const inputUiElement: UiElement = { name: 'Username', @@ -33,15 +46,17 @@ describe('WidgetFactory', () => { minlength: 10 } } + const widgetConfig: WidgetConfig = appConfig.widgets.input + + const inputWidget = new Input(inputUiElement.props, inputUiElement.name, widgetConfig) - const inputWidget = new Input(inputUiElement.props, inputUiElement.name) - expect(widgetFactory.create(inputUiElement)).toEqual(inputWidget) }) it('throw invalid widget error', async () => { const inputUiElement: UiElement = { widget: 'invalid', + name: '', position: 16, props: {} }