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

StreamGallery: Initial implementation #30597

Merged
merged 40 commits into from
Oct 29, 2020

Conversation

caroqliu
Copy link
Contributor

@caroqliu caroqliu commented Oct 9, 2020

This PR adds a StreamGallery Preact component and registers its amp-stream-gallery counterpart. Partial for #28284.

Key takeaways:

  • StreamGallery takes a number of props and, along with the size of its container, calculates a visibleCount of slides to show at once.
  • In 0.1 the component was implemented standalone, whereas in 1.0 the component instantiates a BaseCarousel under the hood. This decreases the implementation surface area significantly.
  • Five props are unique to StreamGallery: minItemWidth, maxItemWidth, minVisibleCount, maxVisibleCount, and peek -- these are taken together, along with the number of items and dimensions of the component, to calculate the visibleCount described above. This is then passed to the underlying BaseCarousel.
  • The following props are still needed on BaseCarousel for this component to work: visibleCount, snapAlign, advanceCount.
  • In 0.1, amp-stream-gallery took an attribute called outset-arrows to allow displaying the carousel arrows on either side of the carousel rather than overlaid on top of the slides. I'd like to propose that this attribute also be added to amp-base-carousel so that this can be configurable at that layer rather than unique to amp-stream-gallery. -- This PR demonstrates the additional logic required (given to amp-base-carousel if inset, and provided separately if outset) if we do not do this. EDIT: outset-arrows has been added to amp-base-carousel so for amp-stream-gallery, it has become yet another prop to pass through.

In a follow up PR I will be adding snapAlign to BaseCarousel and slideAlign to StreamGallery.

@google-cla google-cla bot added the cla: yes label Oct 9, 2020
extensions/amp-stream-gallery/1.0/stream-gallery.js Outdated Show resolved Hide resolved
extensions/amp-stream-gallery/1.0/stream-gallery.js Outdated Show resolved Hide resolved
extensions/amp-stream-gallery/1.0/stream-gallery.js Outdated Show resolved Hide resolved
if (!galleryRef.current) {
return {visibleCount: 1, maxContainerWidth: Number.MAX_VALUE};
}
const width = galleryRef.current./* OK */ offsetWidth;
Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm. This is a bit of a trouble. It's not a code we can run on SSR side. And also means that we need to listen to resizes. I understand that in SSR mode it should never get this far b/c of the if above. But it feels cryptic. Also, might be a source of CLS violations. Any other way to express it? At the very least we could set it up as a state+effect-with-resize like fit-text.

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 refactored this a bit, could you take another look and clarify concerns if they remain?

@caroqliu caroqliu marked this pull request as ready for review October 22, 2020 21:22
Comment on lines 43 to 55
// ignore ResizeObserver loop limit exceeded
// this is ok in several scenarios according to
// https://github.com/WICG/resize-observer/issues/38
before(() => {
window.onerror = function (err) {
if (err === 'ResizeObserver loop limit exceeded') {
return false;
} else {
return err;
}
};
});

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is where the error is ignored @dvoytenko

@caroqliu caroqliu requested a review from dvoytenko October 26, 2020 16:40
extensions/amp-stream-gallery/1.0/stream-gallery.js Outdated Show resolved Hide resolved
);

// Adjust visible slide count when container size or parameters change.
useLayoutEffect(() => {
Copy link
Member

Choose a reason for hiding this comment

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

I believe there is a subtle hidden bug in this layoutEffect hook.
Namely that changes to the galleryRef.current pointer would not retrigger the effect.
If it is working, it is because we are getting lucky with the synchronization of the ref setting and when the render is happening.

The solution is to use a callback ref.

In this case, I think that means:

const setupMeasureRef = useCallback(() => { ...  }, [measure]);
return <div ref={setupMeasureRef}> ... </div>;

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 just removed the outer div with the ref in favor of using one provided by the BaseCarousel, does this comment still apply?

Copy link
Member

@samouri samouri Oct 26, 2020

Choose a reason for hiding this comment

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

The issue that useLayoutEffect will not be notified about a change to ref.current.node still exists. The way the code is currently written, I don't think it would be a problem since the nodes on which the refs exist are static (BaseCarousel / ScrollerRef).

If in the future the ref moves to a component or node that is in any way rendered conditionally, then it becomes a bug. If this is a safe assumption, feel free to leave as-is

Copy link
Contributor

Choose a reason for hiding this comment

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

The DOM ref changes is a big headache in React. They are not directly supported by any API. I think the overall idea is, as much as possible, to assume DOM refs do not change unless it's an explicitly supported case.

extensions/amp-stream-gallery/1.0/stream-gallery.js Outdated Show resolved Hide resolved
Copy link
Contributor

@dvoytenko dvoytenko left a comment

Choose a reason for hiding this comment

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

Mostly LGTM, but one caveat on style usage.

);

// Adjust visible slide count when container size or parameters change.
useLayoutEffect(() => {
Copy link
Contributor

Choose a reason for hiding this comment

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

The DOM ref changes is a big headache in React. They are not directly supported by any API. I think the overall idea is, as much as possible, to assume DOM refs do not change unless it's an explicitly supported case.

measurements.maxContainerWidth >= Number.MAX_VALUE
? ''
: measurements.maxContainerWidth,
justifyContent: extraSpace === 'around' ? 'center' : 'initial',
Copy link
Contributor

Choose a reason for hiding this comment

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

This might be an important issue. It's hard to use flexbox in styles because the vendor-prefixing is very complicated. Is there a way to push flexGrow, justifyContent, and similar to JSS? We can always allow a mode on BaseCarousel and let it apply the stylesheet class.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Moved these to JSS, PTAL.

Copy link
Contributor

@dvoytenko dvoytenko left a comment

Choose a reason for hiding this comment

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

One important point, but otherwise LGTM

extensions/amp-stream-gallery/1.0/stream-gallery.js Outdated Show resolved Hide resolved
@caroqliu caroqliu merged commit 318b991 into ampproject:master Oct 29, 2020
ed-bird pushed a commit to ed-bird/amphtml that referenced this pull request Dec 10, 2020
* Add Storybook samples

* Prototype Preact StreamGallery

* Prototype AMP component

* Allow stream gallery to import from base carousel

* Add unit tests

* Remove slideAlign for now

* Sync with latest BaseCarousel changes

* Use ResizeObserver

* Update comment note

* Shrink arrow JSX

* Do not export `Controls`

* Update tests

* Add guard in Scroller class

* Update test for third slide being rendered

* Disallow infinity maxWidth value

* Guard for ResizeObserver testing error

* Clean up logic

* Add AMP CSS and test

* Rename `width` to `containerWidth`

* Use ResizeObserver entry to measure container

* Reset window.onerror

* Remove after hook for now

* Update dependency checks

* Use ref from scroller

* Prefer ?? to ||

* Reset window.onerror

* Return the ref.current.node as the root element

* Inline JSX

* Use the ResizeObserver from the local window

* Guard for ref.current

* Move styles to classes

* getVisibleCount should return visibleCount

* Scroller node not needed

* Fix types

* Update dependency allowlist

* Resolve extraSpace class

* Destructure props in function body
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants