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

Global Notices: support action link, timeout and animation. #4821

Closed
wants to merge 19 commits into
base: master
from

Conversation

Projects
None yet
9 participants
@taggon
Member

taggon commented Apr 18, 2016

This PR enhances Global Notices and attempts to tackle the tasks in #2958. You can go through how I solved each items from #2958 one by one. (The sentence starting with a checkbox is a task @rickybanister listed.)

Global Notices

Stack-up

  • allow more than one notice to stack up, rather than new notices replacing older ones—they should create a nice list

Now the global notices will be stacked up vertically by default. If you want to replace an old notice with new one, read “Replacing a notice” section.

New Options

  • style notice-action to allow for action links inside global notices (they should allow for gridicons as well)
  • allow notices to have either a timeout (to be fully ephemeral, like for 'Saved!' notices)

The notice action creators accept the three extra options:

  • button - Text label to display on action button.
  • duration - Duration in milliseconds to display the notice before dismissing.
  • icon - Any Gridicon's name.

If you want to show a global notice that contains an action button, just set the button option like this:

import { successNotice } from 'state/notices/actions';

/* ... */

successNotice( 'Hello', {
    button: 'Action'
} );

The notice will show an action button whose label says Action. Unfortunately, the redux documentation says:

By convention, the top-level state is an object or some other key-value collection like a Map, but technically it can be any type. Still, you should do your best to keep the state serializable. Don’t put anything inside it that you can’t easily turn into JSON.

So I had to make function-type notice options like old onClick event handler obsolete. Consider using the notice mixin if you need to listen to click event on action buttons.

The durationis the same with the old notices option.

New Animations

  • More fun animation—take a cue from the Reader 'new posts' indicator and have the notices slide in from stage left, and fly out the same way

Now Global Notices supports animation prop that accepts an object indicates enter and leave animations. Available animation types are fade, fade-left, fade-right, fade-up, fade-down and zoom. The following code applys fade-left and fade-right effects on the notice appearing and disappearing respectively.

<GlobalNotices animation={{enter: 'fade-left', leave: 'fade-right'}}> />

Notice Replacement

  • allow notices to be replaced by another notice (like 'Activating Plugin' => 'Plugin Activated')—they would have to be sub-grouped or something

A new notice always replace the old one that has the same ID. Suppose you want to change a text in a global notice whose ID is notice-1 and the new text is "Plugin Activated". You can write the code like this:

import { infoNotice } from 'state/notices/actions';

infoNotice( 'Plugin Activated', { id: 'notice-1' } );

The Reader's New Post Indicator

  • Convert the Reader 'new posts' indicator to be a global notice so it doesn't collide with them.

Now the indicator is a global notice. Because currently we only support success, error, info and warning, is-update status and updateNotice() action creators are newly added to display the indicator.

updated

## Notice Mixin

Notice mixin helps you with creating and managing global notices. I'd recommend you apply the mixin to components that create a global notice. For more detail, read the documentation.

taggon added some commits Apr 17, 2016

Global Notices: some nice to haves (#2958)
- Allow notices to include an action link.
- Allow notices to have a timeout.
- Add 1px of top margin to a notice.
- Improve a bit of performance.
- New notice replaces the old one has the same ID.
- More than one notice will stack up.
- The Reader uses Global Notices for 'new posts' indicator.
- Update the design example to use redux action creators instead of old notice functions.
@rickybanister

This comment has been minimized.

Show comment
Hide comment
@rickybanister

rickybanister Apr 18, 2016

Member

Hey, this is looking pretty good.

I think we should probably use the orange color still for the Reader notice instead of the blue. That matches the notifications dot and seemed to work well before.

screen shot 2016-04-18 at 4 15 09 pm

Member

rickybanister commented Apr 18, 2016

Hey, this is looking pretty good.

I think we should probably use the orange color still for the Reader notice instead of the blue. That matches the notifications dot and seemed to work well before.

screen shot 2016-04-18 at 4 15 09 pm

@taggon

This comment has been minimized.

Show comment
Hide comment
@taggon

taggon Apr 19, 2016

Member

@rickybanister That makes sense.
I restored the orange color and, plus, I added className option to a notice. It will allow you to customize notices.

updated

Member

taggon commented Apr 19, 2016

@rickybanister That makes sense.
I restored the orange color and, plus, I added className option to a notice. It will allow you to customize notices.

updated

@mtias

This comment has been minimized.

Show comment
Hide comment
@mtias

mtias Apr 19, 2016

Member

This is a great approach that cleans up custom reader code and improves global notices.

Calling @artpi as he has spent time thinking and working on these for a deeper look.

Member

mtias commented Apr 19, 2016

This is a great approach that cleans up custom reader code and improves global notices.

Calling @artpi as he has spent time thinking and working on these for a deeper look.

@mtias mtias added the Framework label Apr 19, 2016

@rickybanister

This comment has been minimized.

Show comment
Hide comment
@rickybanister

rickybanister Apr 20, 2016

Member

I agree with @mtias here, maybe we could add an 'is-update' prop for orange notices.

The color would be $orange-jazzy

Member

rickybanister commented Apr 20, 2016

I agree with @mtias here, maybe we could add an 'is-update' prop for orange notices.

The color would be $orange-jazzy

@taggon

This comment has been minimized.

Show comment
Hide comment
@taggon

taggon Apr 21, 2016

Member

@mtias @rickybanister Thanks for your feedbacks. I added update status and updateNotice action creator.

Member

taggon commented Apr 21, 2016

@mtias @rickybanister Thanks for your feedbacks. I added update status and updateNotice action creator.

@taggon

This comment has been minimized.

Show comment
Hide comment
@taggon

taggon Apr 25, 2016

Member

@rickybanister @mtias @artpi Any other suggestions or comments?

Member

taggon commented Apr 25, 2016

@rickybanister @mtias @artpi Any other suggestions or comments?

@rickybanister

This comment has been minimized.

Show comment
Hide comment
@rickybanister

rickybanister Apr 25, 2016

Member

Looks good to me.

@mtias?

Member

rickybanister commented Apr 25, 2016

Looks good to me.

@mtias?

@artpi

This comment has been minimized.

Show comment
Hide comment
@artpi

artpi May 2, 2016

Contributor

수고했어요 !

Nice work @taggon !

I don't think it's a blocker for this PR, but maybe I'll explain whats going on with the notices reduxification effort (which has halted a bit) and why its not done yet:

Actions

In some places (as I recall, around jetpack and store) action or dismiss triggers a function. That is a problem in new reduxified notices, because we don't want functions in notices redux tree. We were discussing with @mtias that we can pass a redux action to trigger once "NoticeAction" is clicked to cover those cases, but that would require listening for those actions also.
Now that @dmsnell has merged #4699 with new analytics middleware, all cases where custom function is used only for analytics can be approached this way.

Components

In some cases, components are injected via translate. We also don't want them in the redux tree

Many global notices use cases that use custom functions and components can be converted to just use notice component and sometimes it would benefit UX if notice appeared in a proper place.

Action creators

Third hurdle to fully convert all notices to new redux tree is that component issuing a notice has to be connected to be wrapped in a provider. Sometimes that has sideeffects and breaks stuff.

Once these 3 problems are dealt with, we will be able to reduce old global notices, clean up a lot of code (like client/notices tree) and simplify stuff.
Just an explanation if you are looking for something to do :)

I intend to go back to these efforts some day, but I can't find time to handle it right now :/

Contributor

artpi commented May 2, 2016

수고했어요 !

Nice work @taggon !

I don't think it's a blocker for this PR, but maybe I'll explain whats going on with the notices reduxification effort (which has halted a bit) and why its not done yet:

Actions

In some places (as I recall, around jetpack and store) action or dismiss triggers a function. That is a problem in new reduxified notices, because we don't want functions in notices redux tree. We were discussing with @mtias that we can pass a redux action to trigger once "NoticeAction" is clicked to cover those cases, but that would require listening for those actions also.
Now that @dmsnell has merged #4699 with new analytics middleware, all cases where custom function is used only for analytics can be approached this way.

Components

In some cases, components are injected via translate. We also don't want them in the redux tree

Many global notices use cases that use custom functions and components can be converted to just use notice component and sometimes it would benefit UX if notice appeared in a proper place.

Action creators

Third hurdle to fully convert all notices to new redux tree is that component issuing a notice has to be connected to be wrapped in a provider. Sometimes that has sideeffects and breaks stuff.

Once these 3 problems are dealt with, we will be able to reduce old global notices, clean up a lot of code (like client/notices tree) and simplify stuff.
Just an explanation if you are looking for something to do :)

I intend to go back to these efforts some day, but I can't find time to handle it right now :/

@@ -97,6 +106,38 @@ module.exports = React.createClass( {
this.showSelectedPost( { replaceHistory: true } );
}
}

This comment has been minimized.

@blowery

blowery May 3, 2016

Contributor

should we also check the updateCount state in componentDidMount, in case the stream renders with updates available? we don't currently automatically accept new posts when mounting the stream.

@blowery

blowery May 3, 2016

Contributor

should we also check the updateCount state in componentDidMount, in case the stream renders with updates available? we don't currently automatically accept new posts when mounting the stream.

This comment has been minimized.

@taggon

taggon May 6, 2016

Member

@blowery Though the problem seems to be outside the scope of this PR, I prefer auto updating too. How about creating a new issue and having discussions there?

@taggon

taggon May 6, 2016

Member

@blowery Though the problem seems to be outside the scope of this PR, I prefer auto updating too. How about creating a new issue and having discussions there?

## Options
The first argument is the text to be displayed on the notice. The second argument is an optional object and accepts some properties:
*All these parameters are optional*
* `id` (default generated `uniqueId()` ) ID for your notice
* `id` (default generated `uniqueId()` ) ID for your notice. If a notice with the same ID already exists then the new one will replace it.

This comment has been minimized.

@nb

nb May 5, 2016

Member

Can we add an example about notices replacing each other? Maybe not with highest priority and I am ok leaving for another PR.

@nb

nb May 5, 2016

Member

Can we add an example about notices replacing each other? Maybe not with highest priority and I am ok leaving for another PR.

This comment has been minimized.

@taggon

taggon May 5, 2016

Member

No problem. I'll add it to the example page.

@taggon

taggon May 5, 2016

Member

No problem. I'll add it to the example page.

@@ -48,6 +48,20 @@ describe( 'reducer', () => {
] );
} );
it( 'should properly replace old notice with new notice that has the same ID', () => {

This comment has been minimized.

@nb

nb May 5, 2016

Member

Thanks for adding a test for this, it made the functionality clearer for me.

@nb

nb May 5, 2016

Member

Thanks for adding a test for this, it made the functionality clearer for me.

@rickybanister

This comment has been minimized.

Show comment
Hide comment
@rickybanister

rickybanister May 6, 2016

Member

I fully agree with you on the point that “it would benefit UX if notice appeared in a proper place”. But I thought Global Notices can be the proper place because notifications in one place would be better than scattered notifications.
And in fact I was tackling the #2958 created by @rickybanister . So I want to hear from @rickybanister too. 😄

Sorry, I think I missed the question. On which feature/bit would you like an opinion?

Member

rickybanister commented May 6, 2016

I fully agree with you on the point that “it would benefit UX if notice appeared in a proper place”. But I thought Global Notices can be the proper place because notifications in one place would be better than scattered notifications.
And in fact I was tackling the #2958 created by @rickybanister . So I want to hear from @rickybanister too. 😄

Sorry, I think I missed the question. On which feature/bit would you like an opinion?

@taggon

This comment has been minimized.

Show comment
Hide comment
@taggon

taggon May 7, 2016

Member

@rickybanister
It is about an item in #2958

You said:

Convert the Reader 'new posts' indicator to be a global notice so it doesn't collide with them.

And @artpi said:

I think that global notices should be used for general events and things that are not on the page.

It seems there is a disagreement on the usage of global notice.

In my opinion, a twitter-like update notice(refer to the screenshot below) for the Reader 'new posts' indicator can meet both requirements: not colliding with global notice, and using global notices for more general events.

twitter_notification

So, what do you think about @artpi’s opinion and mine?

Member

taggon commented May 7, 2016

@rickybanister
It is about an item in #2958

You said:

Convert the Reader 'new posts' indicator to be a global notice so it doesn't collide with them.

And @artpi said:

I think that global notices should be used for general events and things that are not on the page.

It seems there is a disagreement on the usage of global notice.

In my opinion, a twitter-like update notice(refer to the screenshot below) for the Reader 'new posts' indicator can meet both requirements: not colliding with global notice, and using global notices for more general events.

twitter_notification

So, what do you think about @artpi’s opinion and mine?

@taggon

This comment has been minimized.

Show comment
Hide comment
@taggon

taggon May 7, 2016

Member

@artpi

Our intention is to keep redux tree as clean and pure as possible to have it serializable.

Understood. I got an idea about how to fulfill the intention but it will take some time to make it work. I think I can show you the result by tomorrow.

I think that global notices should be used for general events and things that are not on the page. Like API errors and information that form was not saved. But they are bad for providing detailed error messages and suggesting fixes.

I got your point and I agree with you.

Regarding redux Provider:

If Provider or connected components cause troubles, we can use Global Notices with context. Components such as TinyMCE and Popover are using this approach.

What do you think of this approach? If you agree, I will update Global Notices to use context.

Member

taggon commented May 7, 2016

@artpi

Our intention is to keep redux tree as clean and pure as possible to have it serializable.

Understood. I got an idea about how to fulfill the intention but it will take some time to make it work. I think I can show you the result by tomorrow.

I think that global notices should be used for general events and things that are not on the page. Like API errors and information that form was not saved. But they are bad for providing detailed error messages and suggesting fixes.

I got your point and I agree with you.

Regarding redux Provider:

If Provider or connected components cause troubles, we can use Global Notices with context. Components such as TinyMCE and Popover are using this approach.

What do you think of this approach? If you agree, I will update Global Notices to use context.

@rickybanister

This comment has been minimized.

Show comment
Hide comment
@rickybanister

rickybanister May 9, 2016

Member

@taggon Ah, I understand.

So since global notices were born out of the design for the Reader new post indicator we always intended to replace it with one.

They are also used 'on page' in many places as an ephemeral 'Saved' indicator and other things. I wouldn't say they are for 'general events or things not on the page'. They are for things outside of the immediate context/viewport—like if there's an error with something far up on the current page, if an import from another page failed, if there are new items to read off screen.

I'm only referring to the UI however, they indicate to a user that something is happening outside of their current view. Code-wise that may not be 100% accurate, but I think it makes sense to use this component for the Reader.

Member

rickybanister commented May 9, 2016

@taggon Ah, I understand.

So since global notices were born out of the design for the Reader new post indicator we always intended to replace it with one.

They are also used 'on page' in many places as an ephemeral 'Saved' indicator and other things. I wouldn't say they are for 'general events or things not on the page'. They are for things outside of the immediate context/viewport—like if there's an error with something far up on the current page, if an import from another page failed, if there are new items to read off screen.

I'm only referring to the UI however, they indicate to a user that something is happening outside of their current view. Code-wise that may not be 100% accurate, but I think it makes sense to use this component for the Reader.

Global Notices: add notice mixin.
The mixin provides some helper functions that create and remove notices.
With this mixin, we don't need `onClick` option anymore. So I removed it.
@taggon

This comment has been minimized.

Show comment
Hide comment
@taggon

taggon May 11, 2016

Member

@artpi It took more than I thought.
I added notice mixin to remove functions from redux tree. That means the onClick option is removed now. See the documentation for more detail and the Reader new posts indicator[1][2] would be a good example.
I would appreciate your feedback.

Member

taggon commented May 11, 2016

@artpi It took more than I thought.
I added notice mixin to remove functions from redux tree. That means the onClick option is removed now. See the documentation for more detail and the Reader new posts indicator[1][2] would be a good example.
I would appreciate your feedback.

@taggon

This comment has been minimized.

Show comment
Hide comment
@taggon

taggon May 11, 2016

Member

@artpi @ricky Thanks!

I missed cases like "Saved" indicators. So, I looked up more and found out that Calypso’s page-level notices exist in both global-notice and not-a-global-notice type. For instance, while the "Saved" indicator is a global notice in My Profile page, it is a not-a-global-notice in the editor form. Each approach clearly has its own advantages and disadvantages.

  • My Profile
    2016-05-12 1 27 07
  • editor form
    2016-05-12 1 27 50

To change UI we have to spend more time. Plus, it may harm UX. Therefore, when it is hard to tell which one is way better than the other, maintaining the status quo can be more reasonable. Fortunately, the former Reader new post indicator looked like a global notice. Converting it to a global notice hardly change the user experience.

@artpi
How about make a new issue for discussing where to use Global Notices? Because, though it certainly is helpful for me to see what’s going on with the notices reduxification effort, it may be getting too deep for this issue.

Member

taggon commented May 11, 2016

@artpi @ricky Thanks!

I missed cases like "Saved" indicators. So, I looked up more and found out that Calypso’s page-level notices exist in both global-notice and not-a-global-notice type. For instance, while the "Saved" indicator is a global notice in My Profile page, it is a not-a-global-notice in the editor form. Each approach clearly has its own advantages and disadvantages.

  • My Profile
    2016-05-12 1 27 07
  • editor form
    2016-05-12 1 27 50

To change UI we have to spend more time. Plus, it may harm UX. Therefore, when it is hard to tell which one is way better than the other, maintaining the status quo can be more reasonable. Fortunately, the former Reader new post indicator looked like a global notice. Converting it to a global notice hardly change the user experience.

@artpi
How about make a new issue for discussing where to use Global Notices? Because, though it certainly is helpful for me to see what’s going on with the notices reduxification effort, it may be getting too deep for this issue.

@nb

This comment has been minimized.

Show comment
Hide comment
@nb

nb May 12, 2016

Member

@taggon thanks, this is looking really good. The community is going away from mixins, though. React will drop support soon. Can we think of another solution? Few ideas: a wrapper component or just explicitly calling some helper functions in componentDidMount and componentWillUnmount.

Member

nb commented May 12, 2016

@taggon thanks, this is looking really good. The community is going away from mixins, though. React will drop support soon. Can we think of another solution? Few ideas: a wrapper component or just explicitly calling some helper functions in componentDidMount and componentWillUnmount.

@taggon

This comment has been minimized.

Show comment
Hide comment
@taggon

taggon May 12, 2016

Member

@nb Okay, for now a wrapper component sounds great. But let me think about it a bit more.

Member

taggon commented May 12, 2016

@nb Okay, for now a wrapper component sounds great. But let me think about it a bit more.

Global Notices: added getHelpers function.
It allows to use the notice mixin module without mixins.
@taggon

This comment has been minimized.

Show comment
Hide comment
@taggon

taggon May 17, 2016

Member

@nb now we can use the noticeMixin module without mixins.

At first I was considering using a wrapper component. But @artpi said that wrapper components fail a lot of test. So I found the new approach that doesn't use any wrapper component.

Since the mixin is still available, we can use it until Calypso completely drops all mixins.

See the documentation for more detail.

Member

taggon commented May 17, 2016

@nb now we can use the noticeMixin module without mixins.

At first I was considering using a wrapper component. But @artpi said that wrapper components fail a lot of test. So I found the new approach that doesn't use any wrapper component.

Since the mixin is still available, we can use it until Calypso completely drops all mixins.

See the documentation for more detail.

@apeatling

This comment has been minimized.

Show comment
Hide comment
@apeatling

apeatling Jun 3, 2016

Member

@nb What are your thoughts on this one? It's a nice update, can we get it in?

Member

apeatling commented Jun 3, 2016

@nb What are your thoughts on this one? It's a nice update, can we get it in?

@nb

This comment has been minimized.

Show comment
Hide comment
@nb

nb Jun 23, 2016

Member

@apeatling sorry for the delay, it’s pretty much good to go, I can look at merging it early next week.

@taggon, could you rebase and clean up the branch history (remove fix commits, etc.)?

Member

nb commented Jun 23, 2016

@apeatling sorry for the delay, it’s pretty much good to go, I can look at merging it early next week.

@taggon, could you rebase and clean up the branch history (remove fix commits, etc.)?

@lancewillett

This comment has been minimized.

Show comment
Hide comment
@lancewillett

lancewillett Oct 4, 2016

Member

Closing as this is a several months old and possibly abandoned. Feel free to re-open if still valid and in-progress. Other work on notices has happened in the meantime.

Member

lancewillett commented Oct 4, 2016

Closing as this is a several months old and possibly abandoned. Feel free to re-open if still valid and in-progress. Other work on notices has happened in the meantime.

@lancewillett lancewillett deleted the update/2958-improvement-on-global-notice branch Oct 4, 2016

@rickybanister

This comment has been minimized.

Show comment
Hide comment
@rickybanister

rickybanister Oct 4, 2016

Member

It may be more valuable to extract out the interesting and applicable bits into smaller separate PRs—for instance the duration. I know @beaulebens and I have been hoping to make 'Saved!' notices more ephemeral for quite a while.

Maybe I can bribe someone to split it up?

@johnHackworth @tyxla @ebinnion ?

Member

rickybanister commented Oct 4, 2016

It may be more valuable to extract out the interesting and applicable bits into smaller separate PRs—for instance the duration. I know @beaulebens and I have been hoping to make 'Saved!' notices more ephemeral for quite a while.

Maybe I can bribe someone to split it up?

@johnHackworth @tyxla @ebinnion ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment