diff options
| author | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
|---|---|---|
| committer | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
| commit | 3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch) | |
| tree | a44932296ef4a9b71d5f010906253d8c53727726 /addons/mail/static/src/components/follow_button | |
| parent | 0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff) | |
initial commit 2
Diffstat (limited to 'addons/mail/static/src/components/follow_button')
4 files changed, 422 insertions, 0 deletions
diff --git a/addons/mail/static/src/components/follow_button/follow_button.js b/addons/mail/static/src/components/follow_button/follow_button.js new file mode 100644 index 00000000..3c1808cb --- /dev/null +++ b/addons/mail/static/src/components/follow_button/follow_button.js @@ -0,0 +1,93 @@ +odoo.define('mail/static/src/components/follow_button/follow_button.js', function (require) { +'use strict'; + +const useShouldUpdateBasedOnProps = require('mail/static/src/component_hooks/use_should_update_based_on_props/use_should_update_based_on_props.js'); +const useStore = require('mail/static/src/component_hooks/use_store/use_store.js'); + +const { Component } = owl; +const { useState } = owl.hooks; + +class FollowButton extends Component { + /** + * @override + */ + constructor(...args) { + super(...args); + useShouldUpdateBasedOnProps(); + this.state = useState({ + /** + * Determine whether the unfollow button is highlighted or not. + */ + isUnfollowButtonHighlighted: false, + }); + useStore(props => { + const thread = this.env.models['mail.thread'].get(props.threadLocalId); + return { + threadIsCurrentPartnerFollowing: thread && thread.isCurrentPartnerFollowing, + }; + }); + } + + //-------------------------------------------------------------------------- + // Public + //-------------------------------------------------------------------------- + + /** + * @return {mail.thread} + */ + get thread() { + return this.env.models['mail.thread'].get(this.props.threadLocalId); + } + + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + + /** + * @private + * @param {MouseEvent} ev + */ + _onClickFollow(ev) { + this.thread.follow(); + } + + /** + * @private + * @param {MouseEvent} ev + */ + _onClickUnfollow(ev) { + this.thread.unfollow(); + } + + /** + * @private + * @param {MouseEvent} ev + */ + _onMouseLeaveUnfollow(ev) { + this.state.isUnfollowButtonHighlighted = false; + } + + /** + * @private + * @param {MouseEvent} ev + */ + _onMouseEnterUnfollow(ev) { + this.state.isUnfollowButtonHighlighted = true; + } + +} + +Object.assign(FollowButton, { + defaultProps: { + isDisabled: false, + }, + props: { + isDisabled: Boolean, + threadLocalId: String, + }, + template: 'mail.FollowButton', +}); + +return FollowButton; + +}); diff --git a/addons/mail/static/src/components/follow_button/follow_button.scss b/addons/mail/static/src/components/follow_button/follow_button.scss new file mode 100644 index 00000000..36fb60e7 --- /dev/null +++ b/addons/mail/static/src/components/follow_button/follow_button.scss @@ -0,0 +1,27 @@ +// ------------------------------------------------------------------ +// Layout +// ------------------------------------------------------------------ + +.o_FollowButton { + display: flex; +} + +// ------------------------------------------------------------------ +// Style +// ------------------------------------------------------------------ + +.o_FollowButton_follow { + color: gray('600'); +} + +.o_FollowButton_unfollow { + color: gray('600'); + + &.o-following { + color: $green; + } + + &.o-unfollow { + color: $orange; + } +} diff --git a/addons/mail/static/src/components/follow_button/follow_button.xml b/addons/mail/static/src/components/follow_button/follow_button.xml new file mode 100644 index 00000000..00fc8d65 --- /dev/null +++ b/addons/mail/static/src/components/follow_button/follow_button.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<templates xml:space="preserve"> + + <t t-name="mail.FollowButton" owl="1"> + <div class="o_FollowButton"> + <t t-if="thread.isCurrentPartnerFollowing"> + <button class="o_FollowButton_unfollow btn btn-link" t-att-class="{ 'o-following': !state.isUnfollowButtonHighlighted, 'o-unfollow': state.isUnfollowButtonHighlighted }" t-att-disabled="props.isDisabled" t-on-click="_onClickUnfollow" t-on-mouseenter="_onMouseEnterUnfollow" t-on-mouseleave="_onMouseLeaveUnfollow"> + <t t-if="state.isUnfollowButtonHighlighted"> + <i class="fa fa-times"/> Unfollow + </t> + <t t-else=""> + <i class="fa fa-check"/> Following + </t> + </button> + </t> + <t t-else=""> + <button class="o_FollowButton_follow btn btn-link" t-att-disabled="props.isDisabled" t-on-click="_onClickFollow"> + Follow + </button> + </t> + </div> + </t> + +</templates> diff --git a/addons/mail/static/src/components/follow_button/follow_button_tests.js b/addons/mail/static/src/components/follow_button/follow_button_tests.js new file mode 100644 index 00000000..0c4553c6 --- /dev/null +++ b/addons/mail/static/src/components/follow_button/follow_button_tests.js @@ -0,0 +1,278 @@ +odoo.define('mail/static/src/components/follow_button/follow_button_tests.js', function (require) { +'use strict'; + +const components = { + FollowButton: require('mail/static/src/components/follow_button/follow_button.js'), +}; +const { + afterEach, + afterNextRender, + beforeEach, + createRootComponent, + start, +} = require('mail/static/src/utils/test_utils.js'); + +QUnit.module('mail', {}, function () { +QUnit.module('components', {}, function () { +QUnit.module('follow_button', {}, function () { +QUnit.module('follow_button_tests.js', { + beforeEach() { + beforeEach(this); + + this.createFollowButtonComponent = async (thread, otherProps = {}) => { + const props = Object.assign({ threadLocalId: thread.localId }, otherProps); + await createRootComponent(this, components.FollowButton, { + props, + target: this.widget.el, + }); + }; + + this.start = async params => { + const { env, widget } = await start(Object.assign({}, params, { + data: this.data, + })); + this.env = env; + this.widget = widget; + }; + }, + afterEach() { + afterEach(this); + }, +}); + +QUnit.test('base rendering not editable', async function (assert) { + assert.expect(3); + + await this.start(); + const thread = this.env.models['mail.thread'].create({ + id: 100, + model: 'res.partner', + }); + await this.createFollowButtonComponent(thread, { isDisabled: true }); + assert.containsOnce( + document.body, + '.o_FollowButton', + "should have follow button component" + ); + assert.containsOnce( + document.body, + '.o_FollowButton_follow', + "should have 'Follow' button" + ); + assert.ok( + document.querySelector('.o_FollowButton_follow').disabled, + "'Follow' button should be disabled" + ); +}); + +QUnit.test('base rendering editable', async function (assert) { + assert.expect(3); + + await this.start(); + const thread = this.env.models['mail.thread'].create({ + id: 100, + model: 'res.partner', + }); + await this.createFollowButtonComponent(thread); + assert.containsOnce( + document.body, + '.o_FollowButton', + "should have follow button component" + ); + assert.containsOnce( + document.body, + '.o_FollowButton_follow', + "should have 'Follow' button" + ); + assert.notOk( + document.querySelector('.o_FollowButton_follow').disabled, + "'Follow' button should be disabled" + ); +}); + +QUnit.test('hover following button', async function (assert) { + assert.expect(8); + + this.data['res.partner'].records.push({ id: 100, message_follower_ids: [1] }); + this.data['mail.followers'].records.push({ + id: 1, + is_active: true, + is_editable: true, + partner_id: this.data.currentPartnerId, + res_id: 100, + res_model: 'res.partner', + }); + await this.start(); + const thread = this.env.models['mail.thread'].create({ + id: 100, + model: 'res.partner', + }); + thread.follow(); + await this.createFollowButtonComponent(thread); + assert.containsOnce( + document.body, + '.o_FollowButton', + "should have follow button component" + ); + assert.containsOnce( + document.body, + '.o_FollowButton_unfollow', + "should have 'Unfollow' button" + ); + assert.strictEqual( + document.querySelector('.o_FollowButton_unfollow').textContent.trim(), + 'Following', + "'unfollow' button should display 'Following' as text when not hovered" + ); + assert.containsNone( + document.querySelector('.o_FollowButton_unfollow'), + '.fa-times', + "'unfollow' button should not contain a cross icon when not hovered" + ); + assert.containsOnce( + document.querySelector('.o_FollowButton_unfollow'), + '.fa-check', + "'unfollow' button should contain a check icon when not hovered" + ); + + await afterNextRender(() => { + document + .querySelector('.o_FollowButton_unfollow') + .dispatchEvent(new window.MouseEvent('mouseenter')); + } + ); + assert.strictEqual( + document.querySelector('.o_FollowButton_unfollow').textContent.trim(), + 'Unfollow', + "'unfollow' button should display 'Unfollow' as text when hovered" + ); + assert.containsOnce( + document.querySelector('.o_FollowButton_unfollow'), + '.fa-times', + "'unfollow' button should contain a cross icon when hovered" + ); + assert.containsNone( + document.querySelector('.o_FollowButton_unfollow'), + '.fa-check', + "'unfollow' button should not contain a check icon when hovered" + ); +}); + +QUnit.test('click on "follow" button', async function (assert) { + assert.expect(7); + + this.data['res.partner'].records.push({ id: 100, message_follower_ids: [1] }); + this.data['mail.followers'].records.push({ + id: 1, + is_active: true, + is_editable: true, + partner_id: this.data.currentPartnerId, + res_id: 100, + res_model: 'res.partner', + }); + await this.start({ + async mockRPC(route, args) { + if (route.includes('message_subscribe')) { + assert.step('rpc:message_subscribe'); + } else if (route.includes('mail/read_followers')) { + assert.step('rpc:mail/read_followers'); + } + return this._super(...arguments); + }, + }); + const thread = this.env.models['mail.thread'].create({ + id: 100, + model: 'res.partner', + }); + await this.createFollowButtonComponent(thread); + assert.containsOnce( + document.body, + '.o_FollowButton', + "should have follow button component" + ); + assert.containsOnce( + document.body, + '.o_FollowButton_follow', + "should have button follow" + ); + + await afterNextRender(() => { + document.querySelector('.o_FollowButton_follow').click(); + }); + assert.verifySteps([ + 'rpc:message_subscribe', + 'rpc:mail/read_followers', + ]); + assert.containsNone( + document.body, + '.o_FollowButton_follow', + "should not have follow button after clicked on follow" + ); + assert.containsOnce( + document.body, + '.o_FollowButton_unfollow', + "should have unfollow button after clicked on follow" + ); +}); + +QUnit.test('click on "unfollow" button', async function (assert) { + assert.expect(7); + + this.data['res.partner'].records.push({ id: 100, message_follower_ids: [1] }); + this.data['mail.followers'].records.push({ + id: 1, + is_active: true, + is_editable: true, + partner_id: this.data.currentPartnerId, + res_id: 100, + res_model: 'res.partner', + }); + await this.start({ + async mockRPC(route, args) { + if (route.includes('message_unsubscribe')) { + assert.step('rpc:message_unsubscribe'); + } + return this._super(...arguments); + }, + }); + const thread = this.env.models['mail.thread'].create({ + id: 100, + model: 'res.partner', + }); + thread.follow(); + await this.createFollowButtonComponent(thread); + assert.containsOnce( + document.body, + '.o_FollowButton', + "should have follow button component" + ); + assert.containsNone( + document.body, + '.o_FollowButton_follow', + "should not have button follow" + ); + assert.containsOnce( + document.body, + '.o_FollowButton_unfollow', + "should have button unfollow" + ); + + await afterNextRender(() => document.querySelector('.o_FollowButton_unfollow').click()); + assert.verifySteps(['rpc:message_unsubscribe']); + assert.containsOnce( + document.body, + '.o_FollowButton_follow', + "should have follow button after clicked on unfollow" + ); + assert.containsNone( + document.body, + '.o_FollowButton_unfollow', + "should not have unfollow button after clicked on unfollow" + ); +}); + +}); +}); +}); + +}); |
