✨ [amp-fit-text] Initial Bento component#28169
Conversation
| const childOb = new MutationObserver(() => { | ||
| this.mutateProps(dict({})); | ||
| }); | ||
| childOb.observe(this.element, {childList: true}); |
There was a problem hiding this comment.
This should be subtree observer I believe to deal with <ul> and other markup nuances. And we should also limit it to the attributes we look for in children. E.g. only with "option" and "selected", right?
There was a problem hiding this comment.
Oops. I shouldn't be reviewing both amp-fit-text and amp-selector at the same time. attributesFilter is not needed here. However, this should still be subtree, right?
There was a problem hiding this comment.
Ya, good catch. But I think we need to resize on any childlist mutation? That could include child attr mutations, too.
There was a problem hiding this comment.
To summarize:
- Add
childListfor changes in the target node - Add
subtreefor changes on descendants of the target node - Add
attributesfor attr changes all along this tree ^
There was a problem hiding this comment.
Is there any need to throttle the callback in case there are many DOM mutations occurring in quick succession? Does the browser handle this?
There was a problem hiding this comment.
I'd not throttle, browser manages the load for us: we will just have several mutation records in the array - though we don't really care how many of them are - one or many we do the same thing.
There was a problem hiding this comment.
Having both subtree and attributes present is causing the observer callback to fire infinitely due to the i-amphtml-context attribute being re-applied on every render. Going to remove one of these for now in favor of improving this separately later on.
There was a problem hiding this comment.
Can we simply ignore mutation records with attribute = i-amphtml-context? Or even with any startsWith(attribute, 'i-amphtml-')?
There was a problem hiding this comment.
Yes, would it be costly to filter every set of mutations for this?
| props['width'] = this.element./* OK */ offsetWidth; | ||
| props['height'] = this.element./* OK */ offsetHeight; |
There was a problem hiding this comment.
@dvoytenko @jridgewell FYI since I know we prefer to use AmpFoo['props'] whenever possible -- I discovered from the e2e tests that the font size should be sized to the offset dimensions of the element if we are to stay consistent with 0.1
There was a problem hiding this comment.
I think sizing to offset dimensions was always a little bit wrong. The right dimension are clientWidth/Height. But we can make it so it doesn't matter.
There was a problem hiding this comment.
But ultimately, can we just drop this code entirely and not pass width and height explicitly?
I think there are now couple of threads on this review on this subject, so please feel free to dedup. But I'll make more targetted comments over the pull request on the subject in this pass.
There was a problem hiding this comment.
On one hand, we are saying "the dev should specify width/height via styles" and on the other we are saying we should not explicitly pass dimensions from AMP mode. In this case we are the dev who should specify the dimensions. I'm OK with doing this via prop.style instead of via prop but I don't think we can get away with doing neither.
There was a problem hiding this comment.
Certainly. AMP would just pass style={width:100%, height:100%}, right?
There was a problem hiding this comment.
The only thing this changes is the e2e test case wherein the expected font size accounts for the border dimensions. Are we OK with breaking that?
jridgewell
left a comment
There was a problem hiding this comment.
I think this looks good.
|
Hey @erwinmombay, @jridgewell! These files were changed: |
| props['width'] = this.element./* OK */ offsetWidth; | ||
| props['height'] = this.element./* OK */ offsetHeight; |
There was a problem hiding this comment.
But ultimately, can we just drop this code entirely and not pass width and height explicitly?
I think there are now couple of threads on this review on this subject, so please feel free to dedup. But I'll make more targetted comments over the pull request on the subject in this pass.
| }, [resize]); | ||
|
|
||
| // Font size should readjust when content changes. | ||
| useLayoutEffect(() => { |
There was a problem hiding this comment.
This one we get for free on the first resize.
There was a problem hiding this comment.
Yes that's right, are you suggesting fine tuning more?
There was a problem hiding this comment.
I meant that we don't need useLayoutEffect here, do we? If we don't have it, then we also don't need to pass width/height from the outside.
There was a problem hiding this comment.
Changed this to useEffect and moved the width and height deps to the useCallback deps for the resize method.
There was a problem hiding this comment.
I agree with useLayoutEffect vs useEffect. However, here are two important nuances:
childrenalways almost change on re-render, even if they are in fact didn't change. That's how Preact works - they don't want to do item-by-item comparisons on thechildrenarray. So this callback will trigger on each re-render and could be wasteful.- The opposite is also true:
childrendo not need to change for the size to change. E.g. a parent font change could lead to children resize. We could potentially ignore this for awhile, but if we'd get it for free from resize observer, that'd be potentially a good win.
It's fine to defer this as a TODO in the tracker. But it's something that we should deal with very soon.
There was a problem hiding this comment.
We get (2) with the resize callback that is dependent on the min and max font size props, no?
I'll add (1) to the open tasks.
There was a problem hiding this comment.
(2): Not always. Min/max or any other props do not need to change. It's enough for some far away parent to change a font-family which may never even be propagated this far.
There was a problem hiding this comment.
That being said, I'm ok if this is an open task as well.
| const {height: maxHeight, width: maxWidth} = last['contentRect']; | ||
| resize(maxHeight, maxWidth); | ||
| }); | ||
| observer.observe(node); |
There was a problem hiding this comment.
This covers the parent size changes, but not the content changes. E.g. if the updated children are passed with more text, then font-size should get smaller to accommodate. We may either have to do to resize observers, one for contentRef and one for measurerRef. Or maybe just one for measurerRef will suffice. I also commented on the maxHeight issue below for measurerRef. All of these cases, btw, ideally would be storybook cases as well to track regressions.
There was a problem hiding this comment.
I'm not sure how we can track this in storybook since the component is forced to render (as if on first load) every time you change a knob. Is there a way to change state in Storybook and look for reactionary render rather than render caused by Storybook?
There was a problem hiding this comment.
The secondary useLayoutEffect discussed here is for managing content changes.
dvoytenko
left a comment
There was a problem hiding this comment.
Great. This is pretty much at LGTM with few clarifications.


No description provided.