mirror of
https://github.com/discourse/discourse.git
synced 2024-11-23 13:31:44 +08:00
FIX: Some re-render functionality in widgets, added more coverage
This commit is contained in:
parent
c2c4eff08b
commit
98ab64dc89
|
@ -1,6 +1,6 @@
|
|||
import { diff, patch } from 'virtual-dom';
|
||||
import { WidgetClickHook } from 'discourse/widgets/click-hook';
|
||||
import { renderedKey } from 'discourse/widgets/widget';
|
||||
import { renderedKey, queryRegistry } from 'discourse/widgets/widget';
|
||||
|
||||
const _cleanCallbacks = {};
|
||||
export function addWidgetCleanCallback(widgetName, fn) {
|
||||
|
@ -17,7 +17,9 @@ export default Ember.Component.extend({
|
|||
|
||||
init() {
|
||||
this._super();
|
||||
this._widgetClass = this.container.lookupFactory(`widget:${this.get('widget')}`);
|
||||
const name = this.get('widget');
|
||||
|
||||
this._widgetClass = queryRegistry(name) || this.container.lookupFactory(`widget:${name}`);
|
||||
this._connected = [];
|
||||
},
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ WidgetClickHook.setupDocumentCallback = function() {
|
|||
while (node) {
|
||||
const widget = node[CLICK_ATTRIBUTE_NAME];
|
||||
if (widget) {
|
||||
return widget.click(e);
|
||||
return widget.rerenderResult(() => widget.click(e));
|
||||
}
|
||||
node = node.parentNode;
|
||||
}
|
||||
|
|
|
@ -18,8 +18,6 @@ export default createWidget('post-gap', {
|
|||
if (state.loading) { return; }
|
||||
state.loading = true;
|
||||
|
||||
this.scheduleRerender();
|
||||
|
||||
const args = { gap: attrs.gap, post: this.model };
|
||||
return this.sendWidgetAction(attrs.pos === 'before' ? 'fillGapBefore' : 'fillGapAfter', args);
|
||||
}
|
||||
|
|
|
@ -401,9 +401,7 @@ export default createWidget('post', {
|
|||
const likeAction = post.get('likeAction');
|
||||
|
||||
if (likeAction && likeAction.get('canToggle')) {
|
||||
const promise = likeAction.togglePromise(post);
|
||||
this.scheduleRerender();
|
||||
return promise;
|
||||
return likeAction.togglePromise(post);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -15,6 +15,10 @@ export function renderedKey(key) {
|
|||
delete _dirty[key];
|
||||
}
|
||||
|
||||
export function queryRegistry(name) {
|
||||
return _registry[name];
|
||||
}
|
||||
|
||||
const _decorators = {};
|
||||
|
||||
export function decorateWidget(widgetName, cb) {
|
||||
|
@ -203,7 +207,7 @@ export default class Widget {
|
|||
}
|
||||
}
|
||||
|
||||
sendComponentAction(name, param) {
|
||||
_sendComponentAction(name, param) {
|
||||
const view = this._findAncestorWithProperty('_emberView');
|
||||
|
||||
let promise;
|
||||
|
@ -233,9 +237,7 @@ export default class Widget {
|
|||
}
|
||||
}
|
||||
|
||||
if (promise) {
|
||||
return promise.then(() => this.scheduleRerender());
|
||||
}
|
||||
return this.rerenderResult(() => promise);
|
||||
}
|
||||
|
||||
findAncestorModel() {
|
||||
|
@ -245,19 +247,25 @@ export default class Widget {
|
|||
}
|
||||
}
|
||||
|
||||
sendWidgetAction(name, param) {
|
||||
const widget = this._findAncestorWithProperty(name);
|
||||
if (widget) {
|
||||
const result = widget[name](param);
|
||||
if (result && result.then) {
|
||||
return result.then(() => this.scheduleRerender());
|
||||
} else {
|
||||
this.scheduleRerender();
|
||||
return result;
|
||||
}
|
||||
rerenderResult(fn) {
|
||||
this.scheduleRerender();
|
||||
const result = fn();
|
||||
// re-render after any promises complete, too!
|
||||
if (result && result.then) {
|
||||
return result.then(() => this.scheduleRerender());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
return this.sendComponentAction(name, param || this.findAncestorModel());
|
||||
sendWidgetAction(name, param) {
|
||||
return this.rerenderResult(() => {
|
||||
const widget = this._findAncestorWithProperty(name);
|
||||
if (widget) {
|
||||
return widget[name](param);
|
||||
}
|
||||
|
||||
return this._sendComponentAction(name, param || this.findAncestorModel());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
194
test/javascripts/widgets/widget-test.js.es6
Normal file
194
test/javascripts/widgets/widget-test.js.es6
Normal file
|
@ -0,0 +1,194 @@
|
|||
import { moduleForWidget, widgetTest } from 'helpers/widget-test';
|
||||
import { decorateWidget, createWidget } from 'discourse/widgets/widget';
|
||||
|
||||
moduleForWidget('base');
|
||||
|
||||
widgetTest('widget attributes are passed in via args', {
|
||||
template: `{{mount-widget widget="hello-test" args=args}}`,
|
||||
|
||||
setup() {
|
||||
createWidget('hello-test', {
|
||||
tagName: 'div.test',
|
||||
|
||||
html(attrs) {
|
||||
return `Hello ${attrs.name}`;
|
||||
},
|
||||
});
|
||||
|
||||
this.set('args', { name: 'Robin' });
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
assert.equal(this.$('.test').text(), "Hello Robin");
|
||||
}
|
||||
});
|
||||
|
||||
widgetTest('buildClasses', {
|
||||
template: `{{mount-widget widget="classname-test" args=args}}`,
|
||||
|
||||
setup() {
|
||||
createWidget('classname-test', {
|
||||
tagName: 'div.test',
|
||||
|
||||
buildClasses(attrs) {
|
||||
return ['static', attrs.dynamic];
|
||||
}
|
||||
});
|
||||
|
||||
this.set('args', { dynamic: 'cool-class' });
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
assert.ok(this.$('.test.static.cool-class').length, 'it has all the classes');
|
||||
}
|
||||
});
|
||||
|
||||
widgetTest('buildAttributes', {
|
||||
template: `{{mount-widget widget="attributes-test" args=args}}`,
|
||||
|
||||
setup() {
|
||||
createWidget('attributes-test', {
|
||||
tagName: 'div.test',
|
||||
|
||||
buildAttributes(attrs) {
|
||||
return { "data-evil": 'trout', "aria-label": attrs.label };
|
||||
}
|
||||
});
|
||||
|
||||
this.set('args', { label: 'accessibility' });
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
assert.ok(this.$('.test[data-evil=trout]').length);
|
||||
assert.ok(this.$('.test[aria-label=accessibility]').length);
|
||||
}
|
||||
});
|
||||
|
||||
widgetTest('buildId', {
|
||||
template: `{{mount-widget widget="id-test" args=args}}`,
|
||||
|
||||
setup() {
|
||||
createWidget('id-test', {
|
||||
buildId(attrs) {
|
||||
return `test-${attrs.id}`;
|
||||
}
|
||||
});
|
||||
|
||||
this.set('args', { id: 1234 });
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
assert.ok(this.$('#test-1234').length);
|
||||
}
|
||||
});
|
||||
|
||||
widgetTest('widget state', {
|
||||
template: `{{mount-widget widget="state-test"}}`,
|
||||
|
||||
setup() {
|
||||
createWidget('state-test', {
|
||||
tagName: 'button.test',
|
||||
|
||||
defaultState() {
|
||||
return { clicks: 0 };
|
||||
},
|
||||
|
||||
html(attrs, state) {
|
||||
return `${state.clicks} clicks`;
|
||||
},
|
||||
|
||||
click() {
|
||||
this.state.clicks++;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
assert.ok(this.$('button.test').length, 'it renders the button');
|
||||
assert.equal(this.$('button.test').text(), "0 clicks");
|
||||
|
||||
click(this.$('button'));
|
||||
andThen(() => {
|
||||
assert.equal(this.$('button.test').text(), "1 clicks");
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
widgetTest('widget update with promise', {
|
||||
template: `{{mount-widget widget="promise-test"}}`,
|
||||
|
||||
setup() {
|
||||
createWidget('promise-test', {
|
||||
tagName: 'button.test',
|
||||
|
||||
html(attrs, state) {
|
||||
return state.name || "No name";
|
||||
},
|
||||
|
||||
click() {
|
||||
return new Ember.RSVP.Promise(resolve => {
|
||||
Ember.run.next(() => {
|
||||
this.state.name = "Robin";
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
assert.equal(this.$('button.test').text(), "No name");
|
||||
|
||||
click(this.$('button'));
|
||||
andThen(() => {
|
||||
assert.equal(this.$('button.test').text(), "Robin");
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
widgetTest('widget attaching', {
|
||||
template: `{{mount-widget widget="attach-test"}}`,
|
||||
|
||||
setup() {
|
||||
createWidget('test-embedded', { tagName: 'div.embedded' });
|
||||
|
||||
createWidget('attach-test', {
|
||||
tagName: 'div.container',
|
||||
html() {
|
||||
return this.attach('test-embedded');
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
assert.ok(this.$('.container').length, "renders container");
|
||||
assert.ok(this.$('.container .embedded').length, "renders attached");
|
||||
}
|
||||
});
|
||||
|
||||
widgetTest('widget decorating', {
|
||||
template: `{{mount-widget widget="decorate-test"}}`,
|
||||
|
||||
setup() {
|
||||
createWidget('decorate-test', {
|
||||
tagName: 'div.decorate',
|
||||
html() {
|
||||
return "main content";
|
||||
},
|
||||
});
|
||||
|
||||
decorateWidget('decorate-test:before', dec => {
|
||||
return dec.h('b', 'before');
|
||||
});
|
||||
|
||||
decorateWidget('decorate-test:after', dec => {
|
||||
return dec.h('i', 'after');
|
||||
});
|
||||
},
|
||||
|
||||
test(assert) {
|
||||
assert.ok(this.$('.decorate').length);
|
||||
assert.equal(this.$('.decorate b').text(), 'before');
|
||||
assert.equal(this.$('.decorate i').text(), 'after');
|
||||
}
|
||||
});
|
Loading…
Reference in New Issue
Block a user