-
-
Notifications
You must be signed in to change notification settings - Fork 116
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Convert FlashObject
to native class
#394
Changes from all commits
9a3ebb2
e66f7a6
7e11a68
51fe1f6
b0afe81
23cd226
27a5fb9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,34 +1,42 @@ | ||
import Evented from '@ember/object/evented'; | ||
import EmberObject, { set } from '@ember/object'; | ||
import { cancel, later } from '@ember/runloop'; | ||
import { guidFor } from '../utils/computed'; | ||
import { isTesting, macroCondition } from '@embroider/macros'; | ||
import { tracked } from '@glimmer/tracking'; | ||
import { guidFor } from '@ember/object/internals'; | ||
import { destroy, isDestroyed, registerDestructor } from '@ember/destroyable'; | ||
|
||
// Disable timeout by default when running tests | ||
const defaultDisableTimeout = macroCondition(isTesting()) ? true : false; | ||
|
||
// Note: | ||
// To avoid https://github.com/adopted-ember-addons/ember-cli-flash/issues/341 from happening, this class can't simply be called Object | ||
export default class FlashObject extends EmberObject.extend(Evented) { | ||
export default class FlashObject { | ||
exitTimer = null; | ||
exiting = false; | ||
@tracked exiting = false; | ||
@tracked message = ''; | ||
gilest marked this conversation as resolved.
Show resolved
Hide resolved
|
||
isExitable = true; | ||
Comment on lines
+12
to
+13
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This this is no longer an EmberObject. Only the tracked properties will update rendered flash messages. We may want to track |
||
initializedTime = null; | ||
|
||
// testHelperDisableTimeout – Set by `disableTimeout` and `enableTimeout` in test-support.js | ||
|
||
get disableTimeout() { | ||
get _disableTimeout() { | ||
return this.testHelperDisableTimeout ?? defaultDisableTimeout; | ||
} | ||
|
||
@(guidFor('message').readOnly()) | ||
_guid; | ||
get _guid() { | ||
return guidFor(this.message?.toString()); | ||
} | ||
|
||
constructor(messageOptions) { | ||
for (const [key, value] of Object.entries(messageOptions)) { | ||
this[key] = value; | ||
} | ||
|
||
gilest marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// eslint-disable-next-line ember/classic-decorator-hooks | ||
init() { | ||
super.init(...arguments); | ||
registerDestructor(this, () => { | ||
this.onDestroy?.(); | ||
|
||
if (this.disableTimeout || this.sticky) { | ||
this._cancelTimer(); | ||
this._cancelTimer('exitTaskInstance'); | ||
}); | ||
|
||
if (this._disableTimeout || this.sticky) { | ||
return; | ||
} | ||
this.timerTask(); | ||
|
@@ -50,25 +58,14 @@ export default class FlashObject extends EmberObject.extend(Evented) { | |
return; | ||
} | ||
this.exitTimerTask(); | ||
this.trigger('didExitMessage'); | ||
this.onDidExitMessage?.(); | ||
} | ||
|
||
willDestroy() { | ||
if (this.onDestroy) { | ||
this.onDestroy(); | ||
} | ||
|
||
this._cancelTimer(); | ||
this._cancelTimer('exitTaskInstance'); | ||
super.willDestroy(...arguments); | ||
} | ||
|
||
preventExit() { | ||
set(this, 'isExitable', false); | ||
this.isExitable = false; | ||
} | ||
|
||
allowExit() { | ||
set(this, 'isExitable', true); | ||
this.isExitable = true; | ||
this._checkIfShouldExit(); | ||
} | ||
|
||
|
@@ -79,27 +76,27 @@ export default class FlashObject extends EmberObject.extend(Evented) { | |
const timerTaskInstance = later(() => { | ||
this.exitMessage(); | ||
}, this.timeout); | ||
set(this, 'timerTaskInstance', timerTaskInstance); | ||
this.timerTaskInstance = timerTaskInstance; | ||
} | ||
|
||
exitTimerTask() { | ||
if (this.isDestroyed) { | ||
if (isDestroyed(this)) { | ||
return; | ||
} | ||
set(this, 'exiting', true); | ||
this.exiting = true; | ||
if (this.extendedTimeout) { | ||
let exitTaskInstance = later(() => { | ||
this._teardown(); | ||
}, this.extendedTimeout); | ||
set(this, 'exitTaskInstance', exitTaskInstance); | ||
this.exitTaskInstance = exitTaskInstance; | ||
} else { | ||
this._teardown(); | ||
} | ||
} | ||
|
||
_setInitializedTime() { | ||
let currentTime = new Date().getTime(); | ||
set(this, 'initializedTime', currentTime); | ||
this.initializedTime = currentTime; | ||
|
||
return this.initializedTime; | ||
} | ||
|
@@ -128,7 +125,7 @@ export default class FlashObject extends EmberObject.extend(Evented) { | |
if (queue) { | ||
queue.removeObject(this); | ||
} | ||
this.destroy(); | ||
this.trigger('didDestroyMessage'); | ||
destroy(this); | ||
this.onDidDestroyMessage?.(); | ||
} | ||
} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,12 +4,14 @@ import { | |
click, | ||
find, | ||
render, | ||
rerender, | ||
settled, | ||
triggerEvent, | ||
} from '@ember/test-helpers'; | ||
import hbs from 'htmlbars-inline-precompile'; | ||
import FlashMessage from 'ember-cli-flash/flash/object'; | ||
import { next, later } from '@ember/runloop'; | ||
import { isDestroyed } from '@ember/destroyable'; | ||
|
||
const timeoutDefault = 1000; | ||
const TIMEOUT = 50; | ||
|
@@ -18,23 +20,29 @@ module('Integration | Component | flash message', function (hooks) { | |
setupRenderingTest(hooks); | ||
|
||
test('it renders a flash message', async function (assert) { | ||
this.set('flash', FlashMessage.create({ message: 'hi', sticky: true })); | ||
const flash = new FlashMessage({ message: 'hi', sticky: true }); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. does the FlashMessage need There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, good idea. I've implemented this in |
||
this.set('flash', flash); | ||
|
||
await render(hbs` | ||
<FlashMessage @flash={{this.flash}} as |component flash|> | ||
{{flash.message}} | ||
</FlashMessage> | ||
`); | ||
|
||
assert.dom('*').hasText('hi'); | ||
assert.dom('*').hasText('hi', 'initial message is displayed'); | ||
|
||
flash.message = 'hello'; | ||
await rerender(); | ||
|
||
assert.dom('*').hasText('hello', 'updated message is displayed'); | ||
}); | ||
|
||
test('it renders with the right props', async function (assert) { | ||
assert.expect(3); | ||
|
||
this.set( | ||
'flash', | ||
FlashMessage.create({ | ||
new FlashMessage({ | ||
message: 'test', | ||
type: 'test', | ||
timeout: TIMEOUT, | ||
|
@@ -68,7 +76,7 @@ module('Integration | Component | flash message', function (hooks) { | |
}); | ||
|
||
test('it does not error when quickly removed from the DOM', async function (assert) { | ||
this.set('flash', FlashMessage.create({ message: 'hi', sticky: true })); | ||
this.set('flash', new FlashMessage({ message: 'hi', sticky: true })); | ||
this.set('flag', true); | ||
|
||
await render(hbs` | ||
|
@@ -82,15 +90,15 @@ module('Integration | Component | flash message', function (hooks) { | |
this.set('flag', false); | ||
|
||
await settled(); | ||
assert.ok(this.flash.isDestroyed, 'Flash Object isDestroyed'); | ||
assert.ok(isDestroyed(this.flash), 'Flash Object isDestroyed'); | ||
}); | ||
|
||
test('flash message is removed after timeout', async function (assert) { | ||
assert.expect(3); | ||
|
||
this.set( | ||
'flash', | ||
FlashMessage.create({ | ||
new FlashMessage({ | ||
message: 'hi', | ||
sticky: false, | ||
timeout: timeoutDefault, | ||
|
@@ -108,7 +116,7 @@ module('Integration | Component | flash message', function (hooks) { | |
() => { | ||
assert.dom('*').hasText('hi'); | ||
assert.notOk( | ||
this.flash.isDestroyed, | ||
isDestroyed(this.flash), | ||
'Flash is not destroyed immediately' | ||
); | ||
}, | ||
|
@@ -117,13 +125,13 @@ module('Integration | Component | flash message', function (hooks) { | |
|
||
await settled(); | ||
|
||
assert.ok(this.flash.isDestroyed, 'Flash Object is destroyed'); | ||
assert.ok(isDestroyed(this.flash), 'Flash Object is destroyed'); | ||
}); | ||
|
||
test('flash message is removed after timeout if mouse enters', async function (assert) { | ||
assert.expect(3); | ||
|
||
let flashObject = FlashMessage.create({ | ||
let flashObject = new FlashMessage({ | ||
message: 'hi', | ||
sticky: false, | ||
timeout: timeoutDefault, | ||
|
@@ -145,7 +153,7 @@ module('Integration | Component | flash message', function (hooks) { | |
|
||
next(this, () => { | ||
assert.notOk( | ||
flashObject.isDestroyed, | ||
isDestroyed(flashObject), | ||
'Flash Object is not destroyed' | ||
); | ||
triggerEvent('#testFlash', 'mouseleave'); | ||
|
@@ -156,15 +164,15 @@ module('Integration | Component | flash message', function (hooks) { | |
|
||
await settled(); | ||
|
||
assert.ok(flashObject.isDestroyed, 'Flash Object is destroyed'); | ||
assert.ok(isDestroyed(flashObject), 'Flash Object is destroyed'); | ||
}); | ||
|
||
test('a custom component can use the close closure action', async function (assert) { | ||
assert.expect(3); | ||
|
||
this.set( | ||
'flash', | ||
FlashMessage.create({ | ||
new FlashMessage({ | ||
message: 'flash message content', | ||
sticky: true, | ||
destroyOnClick: false, | ||
|
@@ -178,21 +186,21 @@ module('Integration | Component | flash message', function (hooks) { | |
</FlashMessage> | ||
`); | ||
|
||
assert.notOk(this.flash.isDestroyed, 'flash has not been destroyed yet'); | ||
assert.notOk(isDestroyed(this.flash), 'flash has not been destroyed yet'); | ||
|
||
await click('.alert'); | ||
assert.notOk(this.flash.isDestroyed, 'flash has not been destroyed yet'); | ||
assert.notOk(isDestroyed(this.flash), 'flash has not been destroyed yet'); | ||
|
||
await click('.alert a'); | ||
assert.ok( | ||
this.flash.isDestroyed, | ||
isDestroyed(this.flash), | ||
'flash is destroyed after clicking close' | ||
); | ||
}); | ||
|
||
test('exiting class is applied for sticky messages', async function (assert) { | ||
assert.expect(2); | ||
let flashObject = FlashMessage.create({ | ||
let flashObject = new FlashMessage({ | ||
message: 'flash message content', | ||
sticky: true, | ||
extendedTimeout: 100, | ||
|
@@ -208,12 +216,12 @@ module('Integration | Component | flash message', function (hooks) { | |
|
||
await click('.alert'); | ||
assert.dom('.alert').hasClass('exiting', 'exiting class is applied'); | ||
assert.ok(flashObject.isDestroyed, 'Flash Object is destroyed'); | ||
assert.ok(isDestroyed(flashObject), 'Flash Object is destroyed'); | ||
}); | ||
|
||
test('custom message type class name prefix is applied', async function (assert) { | ||
assert.expect(2); | ||
let flashObject = FlashMessage.create({ | ||
let flashObject = new FlashMessage({ | ||
message: 'flash message content', | ||
type: 'test', | ||
sticky: true, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assuming next release will be v6, although minor, this is a breaking change.
Would be good to batch all the remaining internal changes and TypeScript migration in the same update...