Add boolean type to template tags (#521)

* Fix multipart newlines for Windows

* Revert multipart

* Fix tests

* Rewrote broken PDF viewer

* Fix Content type active checkmark

* Add boolean arg type for template tags
This commit is contained in:
Gregory Schier
2017-10-13 02:06:51 +02:00
committed by GitHub
parent 8451eae270
commit 3973f13daf
4 changed files with 69 additions and 52 deletions

View File

@@ -116,8 +116,8 @@ describe('tokenizeTag()', () => {
const expected = {
name: 'name',
args: [
{type: 'boolean', value: 'true'},
{type: 'boolean', value: 'false'}
{type: 'boolean', value: true},
{type: 'boolean', value: false}
]
};

View File

@@ -46,6 +46,11 @@ export type PluginArgumentString = PluginArgumentBase & {
defaultValue?: string
};
export type PluginArgumentBoolean = PluginArgumentBase & {
type: 'boolean';
defaultValue?: boolean
};
export type PluginArgumentNumber = PluginArgumentBase & {
type: 'number';
placeholder?: string,
@@ -56,6 +61,7 @@ export type PluginArgument =
PluginArgumentEnum
| PluginArgumentModel
| PluginArgumentString
| PluginArgumentBoolean
| PluginArgumentNumber;
export type PluginTemplateTagContext = {

View File

@@ -3,6 +3,7 @@
export type NunjucksParsedTagArg = {
type: 'string' | 'number' | 'boolean' | 'number' | 'variable' | 'expression',
value: string | number | boolean,
defaultValue?: string | number | boolean,
forceVariable?: boolean,
quotedBy?: '"' | "'"
};
@@ -121,7 +122,7 @@ export function tokenizeTag (tagStr: string): NunjucksParsedTag {
if (quotedBy) {
arg = {type: 'string', value: currentArg, quotedBy};
} else if (['true', 'false'].includes(currentArg)) {
arg = {type: 'boolean', value: currentArg};
arg = {type: 'boolean', value: currentArg.toLowerCase() === 'true'};
} else if (currentArg.match(/^\d*\.?\d*$/)) {
arg = {type: 'number', value: currentArg};
} else if (currentArg.match(/^[a-zA-Z_$][0-9a-zA-Z_$]*$/)) {
@@ -149,6 +150,8 @@ export function unTokenizeTag (tagData: NunjucksParsedTag): string {
const re = new RegExp(`([^\\\\])${q}`, 'g');
const str = arg.value.toString().replace(re, `$1\\${q}`);
args.push(`${q}${str}${q}`);
} else if (arg.type === 'boolean') {
args.push(arg.value ? 'true' : 'false');
} else {
args.push(arg.value);
}
@@ -160,23 +163,21 @@ export function unTokenizeTag (tagData: NunjucksParsedTag): string {
/** Get the default Nunjucks string for an extension */
export function getDefaultFill (name: string, args: Array<NunjucksParsedTagArg>): string {
const stringArgs = (args || []).map(argDefinition => {
if (argDefinition.type === 'enum') {
const {defaultValue, options} = argDefinition;
const value = defaultValue !== undefined ? defaultValue : options[0].value;
return `'${value}'`;
} else if (argDefinition.type === 'number') {
return argDefinition.defaultValue !== undefined ? argDefinition.defaultValue : 0;
} else if (argDefinition.defaultValue !== undefined) {
switch (typeof argDefinition.defaultValue) {
case 'string':
case 'number':
case 'boolean':
return `'${argDefinition.defaultValue.toString()}'`;
}
const stringArgs: Array<string> = (args || []).map(argDefinition => {
switch (argDefinition.type) {
case 'enum':
const {defaultValue, options} = argDefinition;
const value = defaultValue !== undefined ? defaultValue : options[0].value;
return `'${value}'`;
case 'number':
return `${parseFloat(argDefinition.defaultValue) || 0}`;
case 'boolean':
return argDefinition.defaultValue ? 'true' : 'false';
case 'string':
return `'${(argDefinition.defaultValue: any) || ''}'`;
default:
return "''";
}
return "''";
});
return `${name} ${stringArgs.join(', ')}`;

View File

@@ -92,14 +92,12 @@ class TagEditor extends React.PureComponent<Props, State> {
}
async _handleRefresh () {
if (this.state.tagDefinitions) {
await this._update(
this.state.tagDefinitions,
this.state.activeTagDefinition,
this.state.activeTagData,
true
);
}
await this._update(
this.state.tagDefinitions,
this.state.activeTagDefinition,
this.state.activeTagData,
true
);
}
async _refreshModels (workspace: Workspace) {
@@ -198,6 +196,8 @@ class TagEditor extends React.PureComponent<Props, State> {
if (e.currentTarget.type === 'number') {
return this._updateArg(parseFloat(e.currentTarget.value), argIndex);
} else if (e.currentTarget.type === 'checkbox') {
return this._updateArg(e.currentTarget.checked, argIndex);
} else {
return this._updateArg(e.currentTarget.value, argIndex);
}
@@ -282,15 +282,10 @@ class TagEditor extends React.PureComponent<Props, State> {
}
}
// Make rendering take at least this long so we can see a spinner
await delay(300 - (Date.now() - start));
this.setState({
tagDefinitions,
activeTagData,
preview,
error,
rendering: false,
activeTagDefinition: tagDefinition
});
@@ -298,6 +293,13 @@ class TagEditor extends React.PureComponent<Props, State> {
if (!noCallback) {
this.props.onChange(template);
}
// Make rendering take at least this long so we can see a spinner
await delay(300 - (Date.now() - start));
this.setState({
rendering: false,
preview
});
}
renderArgVariable (path: string) {
@@ -335,6 +337,12 @@ class TagEditor extends React.PureComponent<Props, State> {
);
}
renderArgBoolean (checked: boolean) {
return (
<input type="checkbox" checked={checked} onChange={this._handleChange}/>
);
}
renderArgEnum (value: string, options: Array<PluginArgumentEnumOption>) {
const argDatas = this.state.activeTagData ? this.state.activeTagData.args : [];
return (
@@ -417,9 +425,9 @@ class TagEditor extends React.PureComponent<Props, State> {
return null;
}
const value = argData.value.toString();
const strValue = argData.value.toString();
const isVariable = argData.type === 'variable';
const argInputVariable = isVariable ? this.renderArgVariable(value) : null;
const argInputVariable = isVariable ? this.renderArgVariable(strValue) : null;
let argInput;
let isVariableAllowed = false;
@@ -428,20 +436,20 @@ class TagEditor extends React.PureComponent<Props, State> {
const placeholder = typeof argDefinition.placeholder === 'string'
? argDefinition.placeholder
: '';
argInput = this.renderArgString(value, placeholder);
argInput = this.renderArgString(strValue, placeholder);
} else if (argDefinition.type === 'enum') {
const {options} = argDefinition;
argInput = this.renderArgEnum(value, options);
argInput = this.renderArgEnum(strValue, options);
} else if (argDefinition.type === 'model') {
const model = typeof argDefinition.model === 'string' ? argDefinition.model : 'unknown';
const modelId = typeof value === 'string' ? value : 'unknown';
const modelId = typeof strValue === 'string' ? strValue : 'unknown';
argInput = this.renderArgModel(modelId, model);
} else if (argDefinition.type === 'boolean') {
argInput = this.renderArgBoolean(strValue.toLowerCase() === 'true');
} else if (argDefinition.type === 'number') {
isVariableAllowed = true;
const placeholder = typeof argDefinition.placeholder === 'string'
? argDefinition.placeholder
: '';
argInput = this.renderArgNumber(value, placeholder || '');
const placeholder = typeof argDefinition.placeholder === 'string' ? argDefinition.placeholder : '';
argInput = this.renderArgNumber(strValue, placeholder || '');
} else {
return null;
}
@@ -452,20 +460,20 @@ class TagEditor extends React.PureComponent<Props, State> {
typeof argDefinition.displayName === 'function'
) ? fnOrString(argDefinition.displayName, argDatas) : '';
const formControlClasses = classnames({
'form-control': true,
'form-control--thin': argDefinition.type === 'boolean',
'form-control--outlined': argDefinition.type !== 'boolean'
});
return (
<div key={argIndex} className="form-row">
<div className="form-control form-control--outlined">
<label>
<div className={formControlClasses}>
<label data-arg-index={argIndex}>
{fnOrString(displayName, argDatas)}
{argData.type === 'variable' ? (
<span className="faded space-left">(Variable)</span>
) : null}
{help && (
<HelpTooltip className="space-left">{help}</HelpTooltip>
)}
<div data-arg-index={argIndex}>
{argInputVariable || argInput}
</div>
{isVariable && <span className="faded space-left">(Variable)</span>}
{help && <HelpTooltip className="space-left">{help}</HelpTooltip>}
{argInputVariable || argInput}
</label>
</div>
{isVariableAllowed ? (
@@ -550,9 +558,11 @@ class TagEditor extends React.PureComponent<Props, State> {
</label>
</div>
)}
<hr className="hr"/>
<div className="form-row">
<div className="form-control form-control--outlined">
<button type="button"
style={{zIndex: 10, position: 'relative'}}
className="txt-sm pull-right icon inline-block"
onClick={this._handleRefresh}>
refresh <i className={classnames('fa fa-refresh', {'fa-spin': rendering})}/>