Skip to content
This repository has been archived by the owner on Sep 2, 2020. It is now read-only.

New: Footer transforms #53

Merged
merged 51 commits into from Jul 19, 2017
Merged

New: Footer transforms #53

merged 51 commits into from Jul 19, 2017

Conversation

montehurd
Copy link
Collaborator

@montehurd montehurd commented Jul 12, 2017

https://phabricator.wikimedia.org/T164137

As these are consolidated I'm concurrently updating this iOS PR.

New transforms:

  • container
  • menu
  • read more
  • legal

Misc:

  • compatibility for older devices (audit array methods etc)

Needed follow-ups:

  • expand test coverage
  • add demos
  • consider further internals refactors/simplifications.

Feedback comments which I've yet to address or which we haven't come to consensus on yet:
#53 (review)
#53 (review)
#53 (review)
#53 (review)
#53 (review)
#53 (review)
#53 (review)
#53 (review)
#53 (review)
#53 (review)
#53 (review)

@montehurd
Copy link
Collaborator Author

Wondering if we should enable the valid-jsdoc eslint rule...

@niedzielski
Copy link
Contributor

@montehurd, yes! Please enable valid-jsdoc. I'm sorry but I thought we already had it enabled and was mistaken.

@@ -0,0 +1,25 @@

.footer_container {
Copy link
Contributor

Choose a reason for hiding this comment

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

It's fine to punt on this but consider using a common prefix like pagelib-footer-thing.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good idea! Once I get Read more into this PR I'll make a wholesale change.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed! (I didn't change from underscores to dashes yet though)

const elements = document.querySelectorAll(
'#footer_container_menu_heading, #footer_container_readmore, #footer_container_legal'
)
Array.from(elements)
Copy link
Contributor

Choose a reason for hiding this comment

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

I see you already called this out in the todo but consider replacing Array.from() with Array.prototype.slice.call() for old time Android devices.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed with polyfill.

}

.footer_menu_icon_disambiguation {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAAAXNSR0IArs4c6QAAAWRJREFUeAHt1ruOwjAQBVAW8YnbbMHXUdDwj6xWKyigGJLrcaTo0PBwBifH1xofDl4ECBAgQIAAAQIECBAgQIAAAQIECBAgQGCGwNeMSf7m+P4532fNtWSe2/USGRyXTObad4HT+0+9v6QrPuruRu0ICQxXBCDAUCAsl0CAoUBYPqwLf9rVPr0ufK5neXfXt4Wf1Os+DEvgY/ruFX/MU73PSroEVitRjAMsgKphgJVQMQ6wAKqGAVZCxTjAAqgaBlgJFePDz4Gv8806j211/pTA1xVf+L09gVslY6HD6sslcDXdfyFAgKFAWC6BAEOBsLy9CzsHhiu09/L2BDoH7j1C4fPpwgBDgbBcAgGGAmF5exd2DgxXaO/l7Qnc+hzYvQM0kXCLtCcwvL+4vHsHSGC4RAABhgJhuQQCDAXC8uFduPvcFT7v8HJbeDipPyRAgAABAgQIECBAgAABAgQIECBAYK8Cv3qKKmmwPBETAAAAAElFTkSuQmCC);
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder why GitHub is coloring this line differently.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ha I was wondering about this too!

* Extracts array of no-html page issues strings from document.
* @type {FooterMenuItemPayloadExtractor}
*/
const pageIssuesStringsArray = document => {
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems to be a method but uses @type.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

See the FooterMenuItemPayloadExtractor typedef for the details. I think I did this correctly according to the Type definitions box in the docs. Lemme know if not :)

Copy link
Contributor

Choose a reason for hiding this comment

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

👍 this looks good! My mistake!

payloadExtractor() {
switch (this.itemType) {
case MenuItemType.languages:
return null
Copy link
Contributor

Choose a reason for hiding this comment

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

Please use undefined instead of null per earlier discussion.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed!

item.classList.add(iconClass)
}

return document.createDocumentFragment().appendChild(item)
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it intentional to a return a value from the constructor?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed! (Fragment makers can just be functions.)

@@ -9,6 +12,9 @@ import WidenImage from './WidenImage'

export default {
CollapseTable,
FooterContainer,
Copy link
Contributor

Choose a reason for hiding this comment

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

This is great how you have these broken out!

background-position: right top;
}

.footer_legal_licence {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Typo!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed.

}

#pagelib_footer_container_readmore_pages::-webkit-scrollbar {
display: none;
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm sure you tried this already but does overflow: hidden not work?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I removed this - was unused.

* @return {void}
*/
const updateBottomPaddingToAllowReadMoreToScrollToTop = (document, window) => {
const div = document.getElementById('pagelib_footer_container_ensure_can_scroll_to_top')
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider making a symbol for this ID.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ok to hold off on this for now? The template complicates doing this... it's filled with such ids...


/**
* Ensures the 'Read more' section header can always be scrolled to the top of the screen.
* @param {!Document} document
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we want to have a document parameter or can we just use window.document?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed!

const updateBottomPaddingToAllowReadMoreToScrollToTop = (document, window) => {
const div = document.getElementById('pagelib_footer_container_ensure_can_scroll_to_top')
let currentPadding = parseInt(div.style.paddingBottom, 10)
if (isNaN(currentPadding)) { currentPadding = 0 }
Copy link
Contributor

Choose a reason for hiding this comment

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

Since NaN is falsy, I think the preceeding line could just be currentPadding = parseInt(...) || 0

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed!

const div = document.getElementById('pagelib_footer_container_ensure_can_scroll_to_top')
let currentPadding = parseInt(div.style.paddingBottom, 10)
if (isNaN(currentPadding)) { currentPadding = 0 }
const height = div.clientHeight - currentPadding
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need to check border width and margin here too?

fragment.appendChild(tables[i].cloneNode(true))
}
// Remove some element so their text doesn't appear when we use "innerText"
Array.from(fragment.querySelectorAll('.hide-when-compact, .collapsed')).forEach(el => el.remove())
Copy link
Contributor

Choose a reason for hiding this comment

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

Since I ended up with a lot of these in my own code, I ended up adding a polyfill in my patch for querySelectorAll.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I copied your polyfill method over and used it :)


/**
* Type representing kinds of menu items.
* @type {Object}
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm still fuzzy on how these @type / @typedef are intended to work but I think this should be @type {!MenuItemType} and in another comment you'd do @typedef {{name: !string, id: !number}} MenuItemType or

@typedef {object} MenuItemType
@var {!number} languages
@var {!number} lastEdited

Copy link
Collaborator Author

@montehurd montehurd Jul 15, 2017

Choose a reason for hiding this comment

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

How about this? Fixed if that seems ok.

* @return {!string}
*/
iconClass() {
switch (this.itemType) {
Copy link
Contributor

Choose a reason for hiding this comment

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

An alternative to this switch might be:

class MenuItemType {
  constructor(id, iconClass) {
    this._id = id
    this._iconClass = iconClass
  }
  get id() { return this._id }
  get iconClass() { return this._iconClass }
}

const MENU_ITEMS = {
  LANGUAGES: new MenuItemType(1, 'pagelib-footer-menu-icon-languages'),
  LAST_EDITED: new MenuItemType(2, 'pagelib-footer-menu-icon-last-edited'),
  PAGE_ISSUES: new MenuItemType(3, 'pagelib-footer-menu-icon-page-issues'),
  DISAMBIGUATION: new MenuItemType(4, 'pagelib-footer-menu-icon-disambiguation'),
  COORDINATE: new MenuItemType(5, 'pagelib-footer-menu-icon-coordinate')
}

Then the WMFMenuItem.iconClass() implementation becomes return menuItem.iconClass() and you don't have to remember to update it when adding new types.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I chewed on this for a bit... thinking I'd prefer to keep MenuItemType an enum. Also was curious as to the reason for opposition to switch... Thoughts?

Copy link
Contributor

Choose a reason for hiding this comment

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

The reason is the elimination of conditional logic and the potential to fall out of sync between the conditional and the declaration, adding new entries is one line instead of a couple, and everything that embodies a menu item's distinction from other items is in one place.

WRT the discussion on how payloadExtractor() would look after this change, you just tack on the ID getter on to the switch and cases (if it's not possible to also embed this conditional into MenuItemType itself):

  payloadExtractor() {
    switch (this.itemType.id) {
      case MenuItemType.pageIssues.id: return pageIssuesStringsArray
      case MenuItemType.disambiguation.id: return disambiguationTitlesArray
      default: return undefined
    }
  }

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

If it's ok I'd prefer to consider internals refactors in follow-ups. Preferably after I've had a chance to add a few more tests.

/**
* Menu item model.
*/
class WMFMenuItem {
Copy link
Contributor

Choose a reason for hiding this comment

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

Everything in the library will probably be WMF-ish. Any reason not to call this just "MenuItem"?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed!

* @param {!string} headingID
* @param {!Document} document
*/
const setHeading = (headingString, headingID, document) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider consistently placing the document / window parameter in the same position in each method signature unless there's a good reason to deviate.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed!

* @return {void}
*/

let _saveForLaterString = null
Copy link
Contributor

Choose a reason for hiding this comment

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

These globals suggest that a class implementation may be more appropriate. The client can than keep one or more instances of that class as needed.

Copy link
Collaborator Author

@montehurd montehurd Jul 15, 2017

Choose a reason for hiding this comment

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

Good news! I didn't need those globals after all. We were already calling a method from native land to set the save button icon state - I modified it to be passed the text to use for the current state of the save button at the same time.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

So...fixed!

@@ -0,0 +1,63 @@

.pagelib_footer_menu_item {
padding: 10px;
Copy link
Contributor

Choose a reason for hiding this comment

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

Would you mind if we stuck with two-space indentation for CSS too?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed!

* @typedef {number} MenuItemType
*/

// eslint-disable-next-line
Copy link
Contributor

Choose a reason for hiding this comment

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

Please limit this to the rules you want to disable. e.g., eslint-disable-next-line valid-jsdoc.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ah will do.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed!

for (let i = 0; i < tables.length; i++) {
fragment.appendChild(tables[i].cloneNode(true))
}
// Remove some element so their text doesn't appear when we use "innerText"
Copy link
Contributor

Choose a reason for hiding this comment

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

Because you're using a DocumentFragment, you have to manually walk clone and walk this list to remove hidden elements? Would using window.document allow you to avoid the clone and selective deletion? According to the MDN documentation, Node.innerText (as opposed to Node.textContent): "innerText is aware of style and will not return the text of hidden elements, whereas textContent will."

https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

iirc I tried that. I'll add some tests with the tricky cases I found then we can safely try alternatives.

margin-bottom: 10px;
border-radius: 2px;
box-shadow: 0px 2px 4px rgba(200, 204, 209, 0.5);
display:block;
Copy link
Contributor

Choose a reason for hiding this comment

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

Missing a space here between display and block :]

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed!

}

.pagelib_footer_readmore_page_title {
color: #222222;
Copy link
Contributor

Choose a reason for hiding this comment

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

Would you mind if we used #rgb[a] notation here? #222

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed!

}

.pagelib_footer_readmore_bookmark_unfilled {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAABGCAYAAACKeRdbAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAZiS0dEAP8A/wD/oL2nkwAABIdJREFUaN7tmltMVEcYgL+zV1AU5KJAFSkg64OtNd5SW3vRhFTTRGuTXrXxAktFRaWYNNqkapt0DaKlFaWUpNE2PtnENjZprS82JhqVJkRj3QVKIaAg5bqwN3b39OG4RwyiK7KMpud72pmdM/N//+yZM7M50lzrZZknGJ3oAP73AobBhcvfzBUdT1jMy69WPz/xM6AJiEYTEI0mIBpNQDSagGg0AdFoAqLRBESjCYhGExCNJiAaTUA0moBoNAHRaAKi0QREowmIRhMQjSYwHLIMtuNNfHasETmC7wIYHr2LoQSC8Ol3Dfx6sRMAjy/I3vVPo49AukZdwOeX+bjyb/6o6VbrfrvUiccXxGbNwGiQRnW8Uc2J2xuk8Kvau4IPcbamm+3ldXh8wcdTwOkKsPGgg8t2p1q3fnkK65alqOUL13op/LoOl3f0JEZFoNPpx1rq4GpDv1pX+OZUClaksmllKhtXPKXW/+lwUnDQgdMVeDwEWjt95JbYqW12KR3qJHauns4HOVPUNhuWJ1P01jS1fLWhH2upgy6nX6xAc7uX3BI7TW0eAAx6ib3r0lm1OHFI2/eWTmbX6unodMpNXNvsIm+/nfbuATEC9Tc85JbYae30AWAy6tiXn8lrC+KHveaNxYnsWZuO/rbEP60e8vbbudnhG1uB600u8kvt/NujZC/KpOPLzVm8PDv2gdcuWxjPF9YMDHpFQp3FW96xEaip7+fDAw66+5Tfb0y0nvJt2SyYOSHsPpbMiaO0IAuzURm+rctHXomduhZ3ZAUuXXeyuayWPreygsTFGKgoymZ25viHHviFWRMp25JFtFkPQEfvAPmlDv5qdEVG4NyVHrYeqsPtVYJPjDVSWWxhZtq4hw4+xDzLBA5vm0FMtCLR0+9n40EHNfX9YfcRlsCZ6i6Kj9TjG1AeQCkJZqp2WMhIiRpx8CGeyRhPRVE2cTHKrqbPHWBzWS0XrzvDuv6BAqfOd7CrqgF/QNlSpk2JomqHhalJ5kcOPsTMtHFUFltIjDUC4PYG2HaojnNXeh5N4MTZdvYcbSQQVILPTI3m22ILUyYZRy34EBkpSmKS400A+AaCFB+p50x118gEvj/dhu14E/LtzXwoSwkTI7IDB2Bqkvmu2fUHZHZVNXDqfMfDCVT9cpOyH5vV8rOZMVQUZRM7Xh+x4EMkx5uU+ys1GoBAUGbP0UZOnG0PT6D85A0qfr6hludZJlC+9c5KMRYkxhqp/ChbXeFkWcZ2vIkffm8b0lafOte6O1To9wQ5drpV/XLRrFgObMoiyjT2R+cok46c+fFUO/q41aVsNS5c60WSJKodd1Yoabi31195Lg7boEe+KNzeINvL6+46ZwzmnqnNmR/PvvxM4cEDRJt1lG2ZwaJZ995nDRF4/fkEPt8QmQP4SDEbJQ4UZPLqnEn3F1j1UhK716ajE5/4IRj0EjZrBssWJtxb4J0lk9n5fproOO+LXgd716Wz8sU7ByYdwJqcZIrfnjbijscSSYJP1kzn3aW3j6xHfmqRn1TKT7bIkixH8o+/yPMfGVAsFZFJcHoAAAAASUVORK5CYII=);
Copy link
Contributor

Choose a reason for hiding this comment

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

The indent is inconsistent here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed!

}

.pagelib_footer_readmore_page_save {
color: #2C5BC5;
Copy link
Contributor

Choose a reason for hiding this comment

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

One more lowercase case :]

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed!

* @return {void}
*/

const _saveButtonIDPrefix = 'readmore:save:'
Copy link
Contributor

Choose a reason for hiding this comment

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

This looks like a static immutable constant, would you mind if we stuck with UPPER_CASE-like syntax for these. So, SAVE_BUTTON_ID_PREFIX? I guess our functions are also constants but this is constant data at file scope.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Not big on upper-casing WHYAREYOUSHOUTING but I like consistency so will do :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed!

if (readMorePage.terms) {
description = readMorePage.terms.description[0]
}
if ((description === undefined || description.length < 10) && readMorePage.extract) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This could be concisely written as (description || '').length < 10 or (!description || description.length < 10).

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ooh nice! I'll try that.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed!

}
}
}
xhr.onerror = e => {
Copy link
Contributor

Choose a reason for hiding this comment

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

The curly braces do not appear to add any value here and the parameter is unused.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed!

const unfilledClass = 'pagelib_footer_readmore_bookmark_unfilled'
const filledClass = 'pagelib_footer_readmore_bookmark_filled'
button.classList.remove(unfilledClass)
button.classList.remove(filledClass)
Copy link
Contributor

Choose a reason for hiding this comment

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

Since add won't doubly add, consider reducing this to: button.classList.remove(isSaved ? unfilledClass : filledClass).

https://developer.mozilla.org/en-US/docs/Web/API/Element/classList

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Oh sweet good catch!!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Turns out classList remove can be passed multiple class names. I used that rather than adding another ternary.

@montehurd montehurd removed the WIP label Jul 18, 2017
@montehurd montehurd changed the title Footer transforms New: Footer transforms Jul 19, 2017
@niedzielski niedzielski merged commit af78b3a into master Jul 19, 2017
@niedzielski niedzielski deleted the footerTransforms branch July 19, 2017 02:59
@niedzielski
Copy link
Contributor

👍 published as v4.3.0. Please update your integration patch to reference this version (and don't forget to unlink before building the bundle).

@montehurd
Copy link
Collaborator Author

Please update your integration patch to reference this version (and don't forget to unlink before building the bundle)

hehe that's got me a few times :)

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
2 participants