-
Notifications
You must be signed in to change notification settings - Fork 398
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
I5011 improve collection success msg appearance #5113
I5011 improve collection success msg appearance #5113
Conversation
I agree, we should use a css transition to make it fade out, rather than just disappear after 5 seconds. |
Codecov Report
@@ Coverage Diff @@
## master mozilla/addons-frontend#5113 +/- ##
==========================================
+ Coverage 96.7% 96.84% +0.14%
==========================================
Files 214 215 +1
Lines 5062 5172 +110
Branches 1012 1022 +10
==========================================
+ Hits 4895 5009 +114
+ Misses 149 145 -4
Partials 18 18
Continue to review full report at Codecov.
|
hey @bobsilverberg - I've updated to use a transition. Please let me know what you think about this and some reallocation of the spacing (I've adjusted spacing here a bit b/c IMO it still looked a little odd when the space changes): https://monosnap.com/image/mdmViit8JIIB6UPrBLlGP0s7G9dTNf.png also - I noticed the library has updated to v2: |
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.
Nice work, thanks @rebmullin. I didn't know about ReactCSSTransitionGroup
. I assumed we'd just use css transitions manually, but if it makes it easier that's cool.
Regarding the version, that's odd because Greenkeeper usually keeps us up to date on out-of-date dependencies. Are you sure the new version is the latest stable version?
I find the video difficult to view. For ease of verification could you also include just a screenshot of the message when it is on the screen? We should get @pwalm to sign off on the final positioning of that.
I'm not sure what the standard are for transitions, but I imagined something a bit more gradual. I.e., the notice would appear, and remain static for a couple of seconds, and then fade away gradually over the remaining 3 seconds. Again, maybe @pwalm has an opinion on this.
The code looks good, and I just have one question about the test, so this is an r+wc, but we should wait to hear back from @pwalm before merging it.
@@ -61,6 +64,10 @@ describe(__filename, () => { | |||
}); | |||
}); | |||
|
|||
afterEach(() => { | |||
clock.restore(); |
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.
If we're only using the clock in one test, is there a reason we have to do this in afterEach
?
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.
sure
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.
altho - i don't think it would hurt having this here... I am just thinking if another tests is added with a clock..
I also see clock some other files where it appears only to be used 1 time and has this similar set up...
|
||
state = root.state(); | ||
expect(state.addonAddedStatus).toEqual(null); | ||
root.setProps({ hasAddonBeenAdded: 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.
I'm not sure why you're setting the state here again, and I would expect odd results because of this. Was this just a line of code left in by accident?
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.
that's what I thought too but it seems that I need to get the updated state (when I do it this way it works :/)
I also had to setProps too which I thought was odd but again it worked this way. Is it possible this is happening b/c i am missing some other piece of code here?
We could but I think this library helps with transitioning when the html is removed from the DOM.
From what i can tell, v2 has been out for a while (almost a year), altho it does say v1 is still actively maintained here's a screenshot of before and after (to get an idea of spacing adjustments): also - here is an updated video which i * think * might show a little better: I agree the transition seems a little fast. I did look at photon which I think suggests even faster but yeh I agree.. best to see what @pwalm thinks for this scenario |
@@ -89,6 +93,14 @@ export class CollectionManagerBase extends React.Component<Props, State> { | |||
this.setState({ | |||
addonAddedStatus: props.hasAddonBeenAdded ? | |||
ADDON_ADDED_STATUS_SUCCESS : null, | |||
}, () => { |
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.
We decided not to use the setState
second argument in https://github.com/mozilla/addons-frontend/issues/3077.
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.
oh? ok.. let me try another way...
@@ -89,6 +93,14 @@ export class CollectionManagerBase extends React.Component<Props, State> { | |||
this.setState({ | |||
addonAddedStatus: props.hasAddonBeenAdded ? | |||
ADDON_ADDED_STATUS_SUCCESS : null, | |||
}, () => { | |||
if (props.hasAddonBeenAdded) { | |||
setTimeout(() => { |
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.
I think we could inject setTimeout
as a default prop and pass a function that executes directly in the test cases.
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.
hey @willdurand - do you mean something like this?
static defaultProps = {
setTimeout: () => {},
};
I guess I am not totally clear of the benefit of this..? let me know if i am missing something or if something like this is ok:
componentWillReceiveProps(props: Props) {
....
if (hasAddonBeenAdded !== hasAddonBeenAddedNew) {
this.setState({
addonAddedStatus: props.hasAddonBeenAdded ?
ADDON_ADDED_STATUS_SUCCESS : null,
});
}
// If the message status has been set, we'll reset the message status after 5 seconds
if (hasAddonBeenAddedNew && hasAddonBeenAddedNew !== hasAddonBeenAdded) {
setTimeout(this.resetMessageStatus, MESSAGE_RESET_TIME);
}
}
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.
I mean having a prop called setTimeout
that is configured with setTimeout
:D
In the tests, we can pass (func) => func()
to that prop. That way no need to deal with ticks in the tests. The benefit is that it is simpler.
I think we never upgraded by fear of breaking things, but we should... |
Thanks @willdurand. @rebmullin could you open a separate issue for that? |
Looks beautiful! Ship it! |
sure @bobsilverberg. But how would we go about updating this if Greenkeeper didn't? is it just a manually change? |
hey @willdurand, I still had to use ticks so I may be missing something. Can you please take a look at my changes when you can? thanks |
Sorry, here is what I had in mind. Note that we may have to rename the prop from diff --git a/src/amo/components/CollectionManager/index.js b/src/amo/components/CollectionManager/index.js
index 554d44fe3..31cf9c8e7 100644
--- a/src/amo/components/CollectionManager/index.js
+++ b/src/amo/components/CollectionManager/index.js
@@ -78,7 +78,7 @@ type State = {|
export class CollectionManagerBase extends React.Component<Props, State> {
static defaultProps = {
- setTimeout: (fn) => setTimeout(fn, MESSAGE_RESET_TIME),
+ setTimeout,
};
constructor(props: Props) {
@@ -104,7 +104,7 @@ export class CollectionManagerBase extends React.Component<Props, State> {
}
if (hasAddonBeenAddedNew && hasAddonBeenAddedNew !== hasAddonBeenAdded) {
- this.props.setTimeout(this.resetMessageStatus);
+ this.props.setTimeout(this.resetMessageStatus, MESSAGE_RESET_TIME);
}
}
diff --git a/tests/unit/amo/components/TestCollectionManager.js b/tests/unit/amo/components/TestCollectionManager.js
index ff08d05b1..b8a2d7730 100644
--- a/tests/unit/amo/components/TestCollectionManager.js
+++ b/tests/unit/amo/components/TestCollectionManager.js
@@ -44,7 +44,6 @@ const simulateAutoSearchCallback = (props = {}) => {
};
describe(__filename, () => {
- let clock;
let fakeRouter;
let store;
const apiHost = config.get('apiHost');
@@ -55,7 +54,6 @@ describe(__filename, () => {
beforeEach(() => {
fakeRouter = createFakeRouter();
store = dispatchClientMetadata().store;
- clock = sinon.useFakeTimers();
dispatchSignInActions({
lang,
store,
@@ -64,10 +62,6 @@ describe(__filename, () => {
});
});
- afterEach(() => {
- clock.restore();
- });
-
const getProps = ({
collection = createInternalCollection({
detail: createFakeCollectionDetail(),
@@ -707,7 +701,8 @@ describe(__filename, () => {
});
it('displays a notification for 5 seconds after an add-on has been added', () => {
- const root = render({});
+ const setTimeoutSpy = sinon.stub();
+ const root = render({ setTimeout: setTimeoutSpy });
expect(root.find(Notice)).toHaveLength(0);
@@ -715,18 +710,15 @@ describe(__filename, () => {
expect(root.find(Notice)).toHaveLength(1);
expect(root.find(Notice).children()).toHaveText('Added to collection');
+ expect(root).toHaveState('addonAddedStatus', ADDON_ADDED_STATUS_SUCCESS);
+ sinon.assert.calledWith(setTimeoutSpy, root.instance().resetMessageStatus, 5000);
- const state = root.state();
- expect(state.addonAddedStatus).toEqual(ADDON_ADDED_STATUS_SUCCESS);
-
- const reset = () => {
- root.setState({ addonAddedStatus: null });
- };
-
- // After 5 seconds the Notice will go away.
- root.setProps(setTimeout(reset));
+ // Simulate the setTimeout behavior.
+ root.instance().resetMessageStatus();
+ // See: https://github.com/airbnb/enzyme/blob/master/docs/guides/migration-from-2-to-3.md#for-mount-updates-are-sometimes-required-when-they-werent-before
+ root.update();
- clock.tick(6000);
+ expect(root).toHaveState('addonAddedStatus', null);
expect(root.find(Notice)).toHaveLength(0);
});
Does that make sense? |
@willdurand, thanks for helping with this : ) I think it makes sense - basically i think your point is you can call that same function from the test thus avoiding the clock stuff. I also am seeing that I need to dig into advanced testing a bit more :0 |
@@ -72,25 +79,40 @@ type State = {| | |||
|}; | |||
|
|||
export class CollectionManagerBase extends React.Component<Props, State> { | |||
static defaultProps = { | |||
setTimeout, | |||
_window: typeof window !== 'undefined' ? window : {}, |
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.
The alternative would be to bind
the setTimeout
function to window
:
setTimeout: typeof window !== 'undefined' ? window.setTimeout.bind(window) : () => {},
</Notice> | ||
)} | ||
|
||
<ReactCSSTransitionGroup |
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.
I forgot how to use this component.. do we have to keep it all the time in the UI tree? (or can we put it in the condition where Notice
is).
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.
yes - it has to always be in the DOM to work
<ReactCSSTransitionGroup | ||
className="NoticePlaceholder" | ||
component="div" | ||
transitionName="overlay" |
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.
Is it a predefined name or custom?
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.
No i meant to change that... thanks for catching
|
||
expect(root.find(Notice)).toHaveLength(0); | ||
|
||
root.setProps({ hasAddonBeenAdded: true }); | ||
|
||
expect(root.find(Notice)).toHaveLength(1); | ||
expect(root.find(Notice).children()).toHaveText('Added to collection'); | ||
|
||
expect(root).toHaveState('addonAddedStatus', ADDON_ADDED_STATUS_SUCCESS); | ||
sinon.assert.calledWith(setTimeoutSpy, root.instance().resetMessageStatus, 5000); |
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.
I guess here we can use MESSAGE_RESET_TIME
|
||
// Simulate the setTimeout behavior. | ||
root.instance().resetMessageStatus(); | ||
// See: https://github.com/airbnb/enzyme/blob/master/docs/guides/migration-from-2-to-3.md#for-mount-updates-are-sometimes-required-when-they-werent-before |
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.
Maybe we can use this link with a git tag in the url: https://github.com/airbnb/enzyme/blob/enzyme%403.3.0/docs/guides/migration-from-2-to-3.md#for-mount-updates-are-sometimes-required-when-they-werent-before. That way it should always be valid.
@willdurand, thanks for the feedback. how's it look now? |
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.
👍
fixes mozilla/addons#11765
video
https://monosnap.com/image/52gnX55LZiRElcneQSKGCGWiJAHHwN.png
I feel like we should add some transition effect here too...