Work in progress on subscriptions

This commit is contained in:
Tomas Bures 2017-08-13 20:11:58 +02:00
parent d9211377dd
commit e73c0a8b28
42 changed files with 1558 additions and 678 deletions

View file

@ -36,7 +36,8 @@ const FormSendMethod = {
class Form extends Component {
static propTypes = {
stateOwner: PropTypes.object.isRequired,
onSubmitAsync: PropTypes.func
onSubmitAsync: PropTypes.func,
inline: PropTypes.bool
}
static childContextTypes = {
@ -77,7 +78,7 @@ class Form extends Component {
}
} else {
return (
<form className="form-horizontal" onSubmit={::this.onSubmit}>
<form className={props.inline ? 'form-inline' : 'form-horizontal'} onSubmit={::this.onSubmit}>
<fieldset disabled={owner.isFormDisabled()}>
{props.children}
</fieldset>
@ -105,25 +106,53 @@ class Fieldset extends Component {
}
}
function wrapInput(id, htmlId, owner, label, help, input) {
const helpBlock = help ? <div className="help-block col-sm-offset-2 col-sm-10" id={htmlId + '_help'}>{help}</div> : '';
function wrapInput(id, htmlId, owner, label, help, input, inline) {
const className = id ? owner.addFormValidationClass('form-group', id) : 'form-group';
return (
<div className={id ? owner.addFormValidationClass('form-group', id) : 'form-group'} >
<div className="col-sm-2">
<label htmlFor={htmlId} className="control-label">{label}</label>
let helpBlock = null;
if (help) {
helpBlock = <div className={'help-block' + (!inline ? ' col-sm-offset-2 col-sm-10' : '')}
id={htmlId + '_help'}>{help}</div>;
}
let validationBlock = null;
if (id) {
const validationMsg = id && owner.getFormValidationMessage(id);
if (validationMsg) {
validationBlock = <div className={'help-block' + (!inline ? ' col-sm-offset-2 col-sm-10' : '')}
id={htmlId + '_help_validation'}>{validationMsg}</div>;
}
}
const labelBlock = <label htmlFor={htmlId} className="control-label">{label}</label>;
if (inline) {
return (
<div className={className} >
{labelBlock} &nbsp; {input}
{helpBlock}
{validationBlock}
</div>
<div className="col-sm-10">
{input}
);
} else {
return (
<div className={className} >
<div className="col-sm-2">
{labelBlock}
</div>
<div className="col-sm-10">
{input}
</div>
{helpBlock}
{validationBlock}
</div>
{helpBlock}
{id && <div className="help-block col-sm-offset-2 col-sm-10" id={htmlId + '_help_validation'}>{owner.getFormValidationMessage(id)}</div>}
</div>
);
);
}
}
function wrapInputInline(id, htmlId, owner, containerClass, label, text, help, input) {
function wrapInputWithText(id, htmlId, owner, containerClass, label, text, help, input) {
const helpBlock = help ? <div className="help-block col-sm-offset-2 col-sm-10" id={htmlId + '_help'}>{help}</div> : '';
const validationMsg = id && owner.getFormValidationMessage(id);
return (
<div className={id ? owner.addFormValidationClass('form-group', id) : 'form-group'} >
@ -134,7 +163,7 @@ function wrapInputInline(id, htmlId, owner, containerClass, label, text, help, i
<label>{input} {text}</label>
</div>
{helpBlock}
{id && <div className="help-block col-sm-offset-2 col-sm-10" id={htmlId + '_help_validation'}>{owner.getFormValidationMessage(id)}</div>}
{id && validationMsg && <div className="help-block col-sm-offset-2 col-sm-10" id={htmlId + '_help_validation'}>{validationMsg}</div>}
</div>
);
}
@ -216,7 +245,7 @@ class CheckBox extends Component {
const id = this.props.id;
const htmlId = 'form_' + id;
return wrapInputInline(id, htmlId, owner, 'checkbox', props.label, props.text, props.help,
return wrapInputWithText(id, htmlId, owner, 'checkbox', props.label, props.text, props.help,
<input type="checkbox" checked={owner.getFormValue(id)} id={htmlId} aria-describedby={htmlId + '_help'} onChange={evt => owner.updateFormValue(id, !owner.getFormValue(id))}/>
);
}
@ -251,7 +280,9 @@ class Dropdown extends Component {
label: PropTypes.string.isRequired,
help: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
options: PropTypes.array,
optGroups: PropTypes.array
optGroups: PropTypes.array,
className: PropTypes.string,
inline: PropTypes.bool
}
static contextTypes = {
@ -276,11 +307,16 @@ class Dropdown extends Component {
);
}
let className = 'form-control';
if (props.className) {
className += ' ' + props.className;
}
return wrapInput(id, htmlId, owner, props.label, props.help,
<select id={htmlId} className="form-control" aria-describedby={htmlId + '_help'} value={owner.getFormValue(id)} onChange={evt => owner.updateFormValue(id, evt.target.value)}>
<select id={htmlId} className={className} aria-describedby={htmlId + '_help'} value={owner.getFormValue(id)} onChange={evt => owner.updateFormValue(id, evt.target.value)}>
{options}
</select>
</select>,
props.inline
);
}
}

View file

@ -60,7 +60,7 @@ h3.legend {
.mt-secondary-nav {
margin: 0px;
background-color: #f5f5f5;
padding: 8px 5px;
padding: 5px 5px;
border-radius: 4px;
}
}

View file

@ -47,7 +47,6 @@ class Table extends Component {
selectionAsArray: PropTypes.bool,
onSelectionChangedAsync: PropTypes.func,
onSelectionDataAsync: PropTypes.func,
actions: PropTypes.func,
withHeader: PropTypes.bool,
refreshInterval: PropTypes.number
}
@ -177,81 +176,82 @@ class Table extends Component {
componentDidMount() {
const columns = this.props.columns.slice();
if (this.props.actions) {
const createdCellFn = (td, data) => {
const linksContainer = jQuery('<span class="mt-action-links"/>');
let actions = this.props.actions(data);
let options = {};
if (!Array.isArray(actions)) {
options = actions;
actions = actions.actions;
}
for (const action of actions) {
if (action.action) {
const html = ReactDOMServer.renderToStaticMarkup(<a href="">{action.label}</a>);
const elem = jQuery(html);
elem.click((evt) => { evt.preventDefault(); action.action(this) });
linksContainer.append(elem);
} else if (action.link) {
const html = ReactDOMServer.renderToStaticMarkup(<a href={action.link}>{action.label}</a>);
const elem = jQuery(html);
elem.click((evt) => { evt.preventDefault(); this.navigateTo(action.link) });
linksContainer.append(elem);
} else if (action.href) {
const html = ReactDOMServer.renderToStaticMarkup(<a href={action.href}>{action.label}</a>);
const elem = jQuery(html);
linksContainer.append(elem);
} else {
const html = ReactDOMServer.renderToStaticMarkup(action.label);
const elem = jQuery(html);
linksContainer.append(elem);
}
}
if (options.refreshTimeout) {
const currentMS = Date.now();
if (!this.refreshTimeoutAt || this.refreshTimeoutAt > currentMS + options.refreshTimeout) {
clearTimeout(this.refreshTimeoutId);
this.refreshTimeoutAt = currentMS + options.refreshTimeout;
this.refreshTimeoutId = setTimeout(() => {
this.refreshTimeoutAt = 0;
this.refresh();
}, options.refreshTimeout);
}
}
jQuery(td).html(linksContainer);
};
columns.push({
data: null,
orderable: false,
searchable: false,
type: 'html',
createdCell: createdCellFn
});
}
// XSS protection
// XSS protection and actions rendering
for (const column of columns) {
const originalRender = column.render;
column.render = (data, ...rest) => {
if (originalRender) {
const markup = originalRender(data, ...rest);
return ReactDOMServer.renderToStaticMarkup(<div>{markup}</div>);
} else {
return ReactDOMServer.renderToStaticMarkup(<div>{data}</div>)
if (column.actions) {
const createdCellFn = (td, data, rowData) => {
const linksContainer = jQuery('<span class="mt-action-links"/>');
let actions = column.actions(rowData);
let options = {};
if (!Array.isArray(actions)) {
options = actions;
actions = actions.actions;
}
for (const action of actions) {
if (action.action) {
const html = ReactDOMServer.renderToStaticMarkup(<a href="">{action.label}</a>);
const elem = jQuery(html);
elem.click((evt) => { evt.preventDefault(); action.action(this) });
linksContainer.append(elem);
} else if (action.link) {
const html = ReactDOMServer.renderToStaticMarkup(<a href={action.link}>{action.label}</a>);
const elem = jQuery(html);
elem.click((evt) => { evt.preventDefault(); this.navigateTo(action.link) });
linksContainer.append(elem);
} else if (action.href) {
const html = ReactDOMServer.renderToStaticMarkup(<a href={action.href}>{action.label}</a>);
const elem = jQuery(html);
linksContainer.append(elem);
} else {
const html = ReactDOMServer.renderToStaticMarkup(<span>{action.label}</span>);
const elem = jQuery(html);
linksContainer.append(elem);
}
}
if (options.refreshTimeout) {
const currentMS = Date.now();
if (!this.refreshTimeoutAt || this.refreshTimeoutAt > currentMS + options.refreshTimeout) {
clearTimeout(this.refreshTimeoutId);
this.refreshTimeoutAt = currentMS + options.refreshTimeout;
this.refreshTimeoutId = setTimeout(() => {
this.refreshTimeoutAt = 0;
this.refresh();
}, options.refreshTimeout);
}
}
jQuery(td).html(linksContainer);
};
column.type = 'html';
column.createdCell = createdCellFn;
if (!('data' in column)) {
column.data = null;
column.orderable = false;
column.searchable = false;
}
};
} else {
const originalRender = column.render;
column.render = (data, ...rest) => {
if (originalRender) {
const markup = originalRender(data, ...rest);
return ReactDOMServer.renderToStaticMarkup(<div>{markup}</div>);
} else {
return ReactDOMServer.renderToStaticMarkup(<div>{data}</div>)
}
};
}
column.title = ReactDOMServer.renderToStaticMarkup(<div>{column.title}</div>);
}

View file

@ -88,11 +88,13 @@ class TreeTable extends Component {
// XSS protection
sanitizeTreeData(unsafeData) {
const data = unsafeData.slice();
for (const entry of data) {
const data = [];
for (const unsafeEntry of unsafeData) {
const entry = Object.assign({}, unsafeEntry);
entry.title = ReactDOMServer.renderToStaticMarkup(<div>{entry.title}</div>)
entry.description = ReactDOMServer.renderToStaticMarkup(<div>{entry.description}</div>)
entry.children = this.sanitizeTreeData(entry.children);
data.push(entry);
}
return data;
}