Skip to content
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

Feature: Make it possible to configure if admin bar loads on page load #1449

Merged
merged 23 commits into from Jul 6, 2018

Conversation

houmark
Copy link
Contributor

@houmark houmark commented Jun 26, 2018

This PR introduces two new configuration options in app.js:

modules: {
  'apostrophe-admin-bar': {
    openOnLoad: false,          // default true
    openOnHomepageLoad: true,           // default true
    closeDelay: 5000           // default 3000 (ms)
  },
    ......
};

Features:

  • It's possible to set openOnLoad to false, making the admin bar stay closed on page loads. If not set defaults to true, opening the admin bar on every page load.
  • It's possible to configure the timeout until the admin bar closes after load if openOnLoad is true or undefined / not set. closeDelay configures the time in milliseconds before the admin bar closes. If not set or set to 0, it will default back to 3000.
  • Makes it possible to open on the homepage using openOnHomepage, but still keep it closed on subpages of the site.

Thoughts:

I thought about making it possible to set closeDelay to 0 and make it stay open infinite or until the user clicks the Apostrophe logo, but decided to not complicate the code further unless there's a want from core devs for this. In that case, let me know and I will update this PR.

This needs a bit of documentation, but I am not sure how that works, I hope the above text helps adding it for whoever has access/knowledge.

Add new configuration to control if admin bar opens on page load.
@houmark
Copy link
Contributor Author

houmark commented Jun 26, 2018

After a bit of sleep I'm considering to rename stayOpenTimeout to closeDelay or something like that. I am open to suggestions, please let me know what's your preference and I will update this PR.

@stuartromanek
Copy link
Member

Hey @houmark this is awesome work.

The Apostrophe team had enhancement to this feature in mind and it is slightly more than what you have here, maybe you'd be willing to work it into this branch?

in addition to a binary open/close state, we had planned to implement a 'only open on the homepage' mode that would be open by default and close with a timeout.

I think this would fit on top of your implementation, double checking another flag and comparing it to window.location.pathname === '/' and using the fallback stayOpenTimeout value of 3000 for the timeout.

What do you think about adding this?

@houmark
Copy link
Contributor Author

houmark commented Jun 26, 2018

Thanks for your feedback @stuartromanek.

I would, but I am not sure checking window.location.pathname === '/' is enough as prefix may come into play here? Also, people could "wrongfully" link to the home page with a path of the home page? Maybe there's a better way to check if this page is the outer most page?

Do you have an opinion on the infinite open which would also overrule this new home page setting?

@stuartromanek
Copy link
Member

Didn't think pathname was sufficient either.. will dig around a bit more to see if there is a better way to know you're on the homepage from the front-end.

As for infinite open, it is not a core want right now but since we're going down this road I'd like to have it. It would make a lot of sense to use if a dev unfloats the admin bar and puts it back in flow as an element.

@abea
Copy link
Contributor

abea commented Jun 26, 2018

Would applying some data attribute if the page had no ancestors work?

@houmark
Copy link
Contributor Author

houmark commented Jun 26, 2018

@abea Yes. There's no way to pass on the current page "meta data" from the admin-bar module as it doesn't run on page load, but only on app start, so it's JavaScript only, which means some other part of Apostrophe would need to add some kind of boolean for the user.js JavaScript to look for inside the DOM.

I do have a window.location.pathname === '/' working now, but that's probably only because my test site does not have any prefix. I will commit that in, pending help from someone in your end to add the more safe data attribute.

Add `openOnHomepage` option which defaults to true and will keep the admin bar open on the front page using the `closeDelay` (default 3000ms) value (needs improvement, see comments in PR).

Renamed `stayOpenTimeout` to `closeDelay` as I think it's a better name.
@abea
Copy link
Contributor

abea commented Jun 26, 2018

I'm thinking that in apostrophe-pages there'd be a beforeShow function checking on the page's ancestors, then passing a property through to base.html or whatever high level template.

@houmark
Copy link
Contributor Author

houmark commented Jun 26, 2018

@abea I'll take a look. @stuartromanek would it be accepted to put a data attribute on the body tag of all pages or at least the home page for this feature to make it in? I don't see any other safe element to add it to, the only other option being html

This will keep the admin bar option infinitely.
@stuartromanek
Copy link
Member

adding data-apos-home to the body of lib/modules/apostrophe-templates/views/outerLayoutBase.html should be sufficient.

@houmark
Copy link
Contributor Author

houmark commented Jun 26, 2018

I was considering to use the internal existing req.data.aposBodyClasses to set a class on the body tag of the home page only. Currently trying to find the best place to determine what is the home page to set that value in apostrophe-pages. It's some hardcore stuff going on there, so I'm not making much progress. I could use a 5 min session with @boutell to point me to the right place to add this.

Adding the data-apos-home is another option which would require a template update, which may not be a problem, but could be for people that have completely overwritten the outerLayoutBase.html, but I guess adding a new block to the outerLayoutBase.html could work?

@houmark
Copy link
Contributor Author

houmark commented Jun 26, 2018

Please see latest commit.

I decided to use the body class for now. I think the place I am hooking into the core is pretty much okay, but I am curious to hear feedback from @boutell on that. Open for changing this concept, but not sure how to add the block and write to it from where I am detecting level 0 in the api of apostrophe-pages and also concerned about backwards compatibility with that approach. Also Apostrophe core itself has yet to use req.data.aposBodyClass and I think this is a very good use case. The added side effect is that it's more simple for devs to use this class in the code (I know that would also be possible with the attribute, but a class is more "known").

Awaiting feedback, but considering this feature complete for now.

@stuartromanek
Copy link
Member

hey @houmark , our house practice is to using data attributes on DOM elements being monitored by JS, it's a reminder to a developer looking at a template that more is happening here beyond CSS styling.

@boutell thoughts on placement? a similar hook for programmatically adding data attrs?

@houmark
Copy link
Contributor Author

houmark commented Jun 26, 2018

Updated code to use "house practice".

(would be great with some core guidelines and best practices documentation page when contributing — but I'm sure that's on the todo list)

@stuartromanek
Copy link
Member

openOnLoad: false being overridden by the baked-in default openOnHomepage: true doesn't feel quite right because I am explicitly shutting it off.

I feel like the flow should be

  • Default (no dev setting): openOnHomepage behavior
  • openOnLoad: false: admin bar never opens
  • openOnLoad: true: admin bar always opens

Not sure if the case of openOnHomepage: false + openOnLoad: true needs to be addressed, but maybe yes for completeness?

@Runyx @grdunn @abea can you weigh in defaults and this API?

@@ -19,8 +19,14 @@ apos.define('apostrophe-admin-bar', {

self.enhance = function() {
var $bar = $('[data-apos-admin-bar]');
var isHomepage = $('[data-apos-home]');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

prefix this var with $ to denote jQuery object

@boutell
Copy link
Member

boutell commented Jun 27, 2018 via email

@abea
Copy link
Contributor

abea commented Jun 27, 2018

Regarding defaults, are we not worried about a behavior BC break? If not, I think what @stuartromanek wrote looks good. I'm not sure I see a problem with openOnHome:true overriding openOnLoad: false for that page.

If so, we could do:

  • Default (no dev setting): openOnLoad: true behavior
  • openOnLoad: false: admin bar never opens
  • openOnLoad: false, openOnHome: true : admin bar opens on home but nowhere else

Not as neat, but would resolve a break. It also wouldn't establish opening only on the homepage as the default.

In the case openOnHomepage: false, openOnLoad: true, openOnLoad should win.

@houmark
Copy link
Contributor Author

houmark commented Jun 27, 2018

If we're going to support pushing data attributes on the body element, and I see Stuart's argument there, then we need to introduce a new method apos.templates.addBodyDataAttribute which would take both an attribute name and a value. The data- prefix would be added automatically.

@boutell Thanks for the hint on the templates API function. I agree on introducing a more generic core API to add this as it can be used by anything else also. I can add this API, but would you accept it as a part of this PR, or should it be its own PR? This PR would rely on the other one being added, so it would complicate testing more.

@houmark
Copy link
Contributor Author

houmark commented Jun 27, 2018

@abea Your latest comment on default behavior is, unless I missed a detail, how it currently works and I did it like that to ensure backwards compatibility.

To sum up: If you don't add anything to app.js, then it will continue to work exactly as before. But the new option openOnHomepage is default to true and that overwrites the behavior for the homepage only, which means it will still open with the default 3000ms timeout, if you don't add any setting.

So to completely disable auto opening, you add both onOnLoad: false; and openOnHomepage: false;. This was how I understood the added homepage feature from @stuartromanek but I may have gotten his idea wrong.

In any case, I can make openOnLoad become the superseding configuration here which means you would only need to set openOnLoad: false; to fully disable the auto load on any page including the homepage.

Again, if you don't change anything when upgrading Apostrophe, things will continue to work as now; admin bar opens on all pages with a delay of 3000ms before they close.

I personally like it as it is right now, because it gives you the most flexibility in configuration. You can keep it closed on all subpages, but still open it on the front page, which helps new editors to understand "what this icon is all about". I even considered mixing this with user/group based settings for the logged in user, but decided to keep v1 simple (this would allow for the admin group keeping it always closed, but a normal user group always opened etc.).

@boutell
Copy link
Member

boutell commented Jun 27, 2018 via email

@houmark
Copy link
Contributor Author

houmark commented Jun 27, 2018

Thanks @boutell, got it. This should be ready very soon.

One more question. For people to be able to rely on the apos-homepage I was considering to add a default apos-subpage which will be on all subpages also, now that I'm at it? Would just be the else on the level === 0. Should I?

@boutell
Copy link
Member

boutell commented Jun 27, 2018 via email

@houmark
Copy link
Contributor Author

houmark commented Jun 27, 2018

It could, but since this is attribute and value only, I don't see the need for an object as there's no apparent use case for adding more key/values to the object?

@houmark
Copy link
Contributor Author

houmark commented Jun 27, 2018

Only if you also make it possible to wrap the object in an array, so that you can pass multiple attributes at once. But this makes it a bit more complex and this API function can be called multiple times if you need multiple attributes added (with my latest edition, that I am about to push).

And sorry, I missed your "array of objects" here in my first read which led to my weird comments.

This means it can only receive one attribute per call but it can be called multiple times of course.
req.data.aposBodyDataAttributes = (req.data.aposBodyDataAttributes ? (req.data.aposBodyDataAttributes + ' ') : ' ') +
('data-' + _.trim(bodyAttribute)).split(' ').join(' data-');
'data-' + (!_.isUndefined(value) ? (name + '=' + value + '') : name);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to also check for name not being undefined / empty. Will push another commit for that, which will just return.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question goes. I wanted the value to be wrapped in " (double quotes) but it's being escaped in the body tag. How do I "bless" this internally to prevent that @boutell?

Also trim to avoid rendering a broken attribute
@houmark
Copy link
Contributor Author

houmark commented Jun 27, 2018

I've done the changes requested, added some inline comments here, awaiting feedback.

@houmark
Copy link
Contributor Author

houmark commented Jun 27, 2018

I went all in on this one and made it possible to pass an object, an array or a string (string for backwards compatibility haha, not). I hope you like that approach.

// is attached to `req.data`, where `req.data.aposBodyDataAttributes` is built up
// using `name` as the attribute name which is automatically prepended with "data-"
// and the optional `value` argument
// Also receives a single object or an array of objects with name and value key pairs
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a better use for an object syntax would be an object like:

{
  foo: 'value',
  bar: 'other value'
}

That feels natural for adding attributes. In fact I think the array syntax could be omitted in favor of this.

return;
}
req.data.aposBodyDataAttributes = (req.data.aposBodyDataAttributes ? (req.data.aposBodyDataAttributes + ' ') : ' ') +
'data-' + (!_.isUndefined(attribute.value) && attribute.value.toString().length > 0 ? (attribute.name + ('="' + attribute.value + '"')) : attribute.name);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we're using "safe" here, we need to make sure these values are actually safe. The attribute name and value should be passed through apos.utils.escapeHtml.

@houmark
Copy link
Contributor Author

houmark commented Jun 29, 2018

Okay, so remove the array option, and key/value objects which also can be multiple and thus you can add multiple attributes in one call to the API function? Should I keep the string option to make it possible to just call the function with 3 arguments; req, name, value or do you want this to be object only for a simpler code base?

I will also do apos.utils.escapeHtml on the attribute name and value.

@houmark
Copy link
Contributor Author

houmark commented Jun 29, 2018

@boutell forgot to mention you, please let me know so I can try to get this one right the next time :)

Will still accept a string value for attribute name and value. Array no longer supported, but the object can be multiple key / value pairs.
@houmark
Copy link
Contributor Author

houmark commented Jun 29, 2018

I have now updated the code to accept an object of key / value based pairs. It will still accept 2 arguments (after req) as strings to just pass one data attribute.

I'm once more back to be pending on reviews.

Copy link
Member

@boutell boutell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for this PR - we are close!

@@ -19,8 +19,14 @@ apos.define('apostrophe-admin-bar', {

self.enhance = function() {
var $bar = $('[data-apos-admin-bar]');
var $isHomepage = $('[data-apos-level="0"]').length;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that this is a simple boolean and not a jquery object anymore... which is great... the $ is no longer appropriate. Sorry for the hassle. I have a lot of PRs to look at today or I'd slip this change in myself!

// is attached to `req.data`, where `req.data.aposBodyDataAttributes` is built up
// using `name` as the attribute name which is automatically prepended with "data-"
// and the optional `value` argument
// Also receives a single object or an array of objects with name and value key pairs
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No longer quite accurate, we removed the array syntax but you're still talking about it in the doc comment.


self.addBodyDataAttribute = function(req, name, value = '') {
var values = {};
if (_.isObject(name) && !_.isArray(name) && !_.isFunction(name)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can drop all checks here other than _.isObject(name) as the only allowed syntaxes now are the "name, value" syntax and the object syntax. Ditto the other places _.isArray() is still around, shouldn't need to do that.

@houmark
Copy link
Contributor Author

houmark commented Jul 2, 2018

I think with my latest changes we should be good. I also merged master back into this branch and there should be no issues.

@stuartromanek
Copy link
Member

@boutell are you good on this implementation?

@houmark can you update the branch with master?

@houmark
Copy link
Contributor Author

houmark commented Jul 5, 2018

Branch is now up to date with master. Ready to merge :)

@boutell boutell merged commit 0a8c59d into apostrophecms:master Jul 6, 2018
@boutell
Copy link
Member

boutell commented Jul 6, 2018

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants