[FIX] store: properly handle defaultProps in connected components

Thanks to aku for issue + tests + first solution

closes #245
This commit is contained in:
Géry Debongnie
2019-07-16 14:17:37 +02:00
parent b1d022b583
commit 93d0a7dd46
3 changed files with 159 additions and 3 deletions
+1 -1
View File
@@ -591,7 +591,7 @@ export class Component<T extends Env, Props extends {}, State extends {}> {
* prop object
*/
__applyDefaultProps(props: Object | undefined, defaultProps: Object): Props {
props = props ? Object.create(props) : {};
props = props ? Object.assign({}, props) : {};
for (let propName in defaultProps) {
if (props![propName] === undefined) {
props![propName] = defaultProps[propName];
+5 -2
View File
@@ -52,15 +52,18 @@ export class ConnectedComponent<T extends Env, P, S> extends Component<T, P, S>
}
constructor(parent, props?: any) {
super(parent, props);
const store = this.getStore(this.env);
const ownProps = Object.assign({}, props || {});
const ownProps = this.props || {};
const storeProps = (<any>this.constructor).mapStoreToProps(
store.state,
ownProps,
store.getters
);
const mergedProps = Object.assign({}, props || {}, storeProps);
const mergedProps = Object.assign({}, ownProps, storeProps);
this.props = mergedProps;
(<any>this.__owl__).ownProps = ownProps;
(<any>this.__owl__).currentStoreProps = storeProps;
(<any>this.__owl__).store = store;
+153
View File
@@ -759,3 +759,156 @@ describe("connecting a component to store", () => {
expect(steps).toEqual(["willpatch", "patched"]);
});
});
describe("connected components and default values", () => {
let fixture: HTMLElement;
let env: Env;
beforeEach(() => {
fixture = makeTestFixture();
env = makeTestEnv();
});
afterEach(() => {
fixture.remove();
});
test("can set default values", async () => {
env.qweb.addTemplates(`
<templates>
<div t-name="Greeter">Hello, <t t-esc="props.recipient"/></div>
<div t-name="App"><Greeter/></div>
</templates>
`);
class Greeter extends ConnectedComponent<any, any, any> {
static defaultProps = { recipient: "John" };
}
class App extends Component<any, any, any> {
components = { Greeter };
}
const store = new Store({ state: {} });
(<any>env).store = store;
const app = new App(env);
await app.mount(fixture);
expect(fixture.innerHTML).toBe('<div><div>Hello, John</div></div>');
});
test("can set default values", async () => {
env.qweb.addTemplates(`
<templates>
<div t-name="Greeter">Hello, <t t-esc="props.recipient"/></div>
<div t-name="App"><Greeter recipient="props.initialRecipient"/></div>
</templates>
`);
class Greeter extends ConnectedComponent<any, any, any> {
static defaultProps = { recipient: "John" };
}
class App extends Component<any, any, any> {
components = { Greeter };
}
const store = new Store({ state: {} });
(<any>env).store = store;
const app = new App(env);
await app.mount(fixture);
expect(fixture.innerHTML).toBe('<div><div>Hello, John</div></div>');
await app.__updateProps({ initialRecipient: "James" }, true);
expect(fixture.innerHTML).toBe('<div><div>Hello, James</div></div>');
await app.__updateProps({ initialRecipient: undefined }, true);
expect(fixture.innerHTML).toBe('<div><div>Hello, John</div></div>');
});
test("can set default values (v2)", async () => {
env.qweb.addTemplates(`
<templates>
<div t-name="Message">
<t t-if="props.showId"><t t-esc="props.messageId"/></t>
<t t-esc="props.message.content"/>
</div>
<div t-name="Thread">
<t t-if="props.showMessages">
<Message t-foreach="props.thread.messages" t-as="messageId" messageId="messageId" t-key="messageId"/>
</t>
</div>
<div t-name="App"><Thread threadId="props.threadId"/></div>
</templates>
`);
class Message extends ConnectedComponent<any, any, any> {
static defaultProps = { showId: true };
static mapStoreToProps = function (state, ownProps) {
return {
message: state.messages[ownProps.messageId],
};
};
}
class Thread extends ConnectedComponent<any, any, any> {
components = { Message };
static defaultProps = { showMessages: true };
static mapStoreToProps = function (state, ownProps) {
const thread = state.threads[ownProps.threadId];
return {
thread,
};
};
}
class App extends Component<any, any, any> {
components = { Thread };
static defaultProps = { threadId: 1 };
}
const state = {
threads: {
1: {
messages: [100, 101],
},
2: {
messages: [200],
},
},
messages: {
100: {
content: "Message100"
},
101: {
content: "Message101"
},
200: {
content: "Message200"
}
}
};
const mutations = {
changeMessageContent({ state }, messageId, newContent) {
state.messages[messageId].content = newContent;
}
};
const store = new Store({ state, mutations });
(<any>env).store = store;
const app = new App(env);
await app.mount(fixture);
expect(fixture.innerHTML).toBe('<div><div><div>100Message100</div><div>101Message101</div></div></div>');
await app.__updateProps({ threadId: 2 }, true);
expect(fixture.innerHTML).toBe('<div><div><div>200Message200</div></div></div>');
store.commit('changeMessageContent', 200, "UpdatedMessage200");
await nextTick();
expect(fixture.innerHTML).toBe('<div><div><div>200UpdatedMessage200</div></div></div>');
});
});