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

feat(logo-grid): convert to CSS modules #309

Merged
merged 14 commits into from
Sep 8, 2021
Merged

Conversation

zchsh
Copy link
Contributor

@zchsh zchsh commented Aug 28, 2021

🎟️ Asana Task
πŸ” Preview Link


This PR converts @hashicorp/react-logo-grid to CSS modules. One significant change to enable this shift was the removal of Tippy.js, which has been replaced by a ReachUI Dialog element.

PR Checklist πŸš€

Items in this checklist may not may not apply to your PR, but please consider each item carefully.

  • Add Asana and Preview links above.
  • Conduct thorough self-review.
  • Add or update tests as appropriate.
    - Note: test file had only .todos. For now I've left these as-is, have not added new tests yet.
    - Rationale for not fixing the test todos yet...
    1. component is mostly presentation, and have done a good amount of manual QA
    2. I think we would prefer to keep momentum on CSS modules conversions rather than rethink our testing strategy for this component.
    - ☝️ That being said, loosely held approach here. Very open to sinking some extra time in to get some good tests written here. (I mean I am open to it either way, just a question of whether we do it now, in this PR, or later, as a follow-up)
  • Conduct reasonable cross browser testing for both compatibility and responsive behavior (We have a Sauce Labs account for this, if you don't have access, just ask!).
  • Conduct reasonable accessibility review (use the WAS as a guide or an axe browser plugin until we establish more formal checks).
  • Identify (in the description above) and document (add Asana tasks on this board) any technical debt that you're aware of, but are not addressing as part of this PR.

@changeset-bot
Copy link

changeset-bot bot commented Aug 28, 2021

πŸ¦‹ Changeset detected

Latest commit: dda8ed9

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@hashicorp/react-logo-grid Major

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel
Copy link

vercel bot commented Aug 28, 2021

This pull request is being automatically deployed with Vercel (learn more).
To see the status of your deployment, click below or on the icon next to each commit.

πŸ” Inspect: https://vercel.com/hashicorp/react-components/9NtxC3wRbEUJKTiiaJKjCtuynibN
βœ… Preview: https://react-components-git-zslogo-grid-css-modules-hashicorp.vercel.app

@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Have replaced &times; with this icon for design consistency. Adds a bit of weight but felt this was worth it. In the future we'll swap in a Flight icon here instead of using a local .svg file.

size?: 'small' | 'medium' | 'large'
}

function LogoGrid({
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've converted to Typescript, and have done a pretty significant refactor, largely due to the switch from Tippy to ReachUI.

[s.removeBorders]: removeBorders,
})}
>
{hasLink ? (
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not super proud of this nested ternary, but it felt like a decent way to avoid prop-drilling. Very open to other ideas on how to clean this up πŸ™‡

Copy link
Contributor

Choose a reason for hiding this comment

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

Only idea I have would be to make it a separate function component, then you could use normal if/else statements. But this doesn't really feel that bad honestly.

)
}

function CompanyLogo({ company, color, size }) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Split this component out, similar to in the previous implementation.

theme?: 'light' | 'dark'
}

function DialogTooltip({
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Similar to the Reach-based tooltip created for tabs, I split this out in the hopes that it might someday see re-use. I did not try to consolidate with the tabs tooltip - in this case, we have interactive content in our tooltip, so we need to use a more Dialog-like component.

Comment on lines 51 to 58
/* Odd fix for possible issue with useRect, where
tooltipRect did not seem to be set as expected.
I think this works because it forces a re-render
when "shown" changes, which is exactly when we need
to look at the tooltipRef again and re-measure.
Without this, tooltipRect seems to always be null.
Not actually sure why this works. Need to investigate. */
useEffect(setNow, [shown])
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 help understanding this πŸ˜… It feels like useRect should just work without this, and update properly with the dialog's DOMRect when it is opened (and therefore rendered). I'm not sure why it doesn't. Possibly related to Portal use, as I did test the same thing without Portal and it worked fine. cc @BRKalow @jescalan @thiskevinwang in case y'all have any insight πŸ™‡

/* We don't want scroll lock, as this is more of a tooltip than a dialog.
Due to our tooltip-like positioning, when scroll lock is enabled, the
tooltip can become locked in an offscreen position. */
dangerouslyBypassScrollLock={true}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

On the fence about this. Basically the default & recommended "dialog" behaviour is that scrolling becomes locked when the dialog is visible. In our case thought, I think this is kind of bad - since we're positioning the dialog in a tooltip-like way, it's very possible the dialog will be at least slightly out-of-view and will require scrolling to get to.

An alternate solution I thought of might be to try to auto-scroll the page before we set shown to true, such that the dialog would be ~ centred in the screen. However this seemed like a decent lift, and likely to cause headaches (eg how would we measure the dialog's DOMRect before shown is set?).

I am curious if anyone has any insights on the implications of this disabling. I read through the Reach docs on this topic. Unlike the dangerouslyBypassFocusLock prop, where the downsides seem more clear to me (ie, as a screen reader user it seems like lack of focus lock would really mess things up), I'm unclear on the exact downsides of setting this to false (at least in our use case).

Copy link
Contributor

Choose a reason for hiding this comment

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

agreed and this seems ok to me in this instance

@@ -16,7 +16,6 @@
@import '../packages/toggle/style.css';
@import '../packages/text-input/style.css';
@import '../packages/hero/style.css';
@import '../packages/logo-grid/style.css';
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 never gets old for me, be forewarned I will probably post these comments in every one of my CSS modules PR self-reviews 🀣

integrationLink,
removeBorders,
size = 'small',
className,
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've added a className prop. Rationale here is that a quick SourceGraph search can easily determine whether the old global className was targeted in "overrides" - if so, I immediately add a className prop so that we can, for now, retain these overrides rather than having their replacement block rollout of these components.

In cases where there are no overrides, ie that SourceGraph search comes up empty, I could still see a case for adding a className prop to enable "safe overrides" (as detailed in the Notion doc on CSS). So if I ever find a component that has 0 overrides in the wild, may still be worth adding this πŸ‘

@zchsh zchsh marked this pull request as ready for review August 31, 2021 20:28
@zchsh zchsh requested review from BRKalow and jescalan August 31, 2021 20:29
Copy link
Contributor

@BRKalow BRKalow left a comment

Choose a reason for hiding this comment

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

Fantastic documentation, as usual! Left a note about exploring @reach/popover, which I think might be more appropriate for this pattern.

packages/logo-grid/index.tsx Outdated Show resolved Hide resolved
@@ -0,0 +1,195 @@
import React, { useState, useRef, useEffect } from 'react'
import { DialogOverlay, DialogContent } from '@reach/dialog'
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't mean to throw a wrench in your progress here, but do you think https://www.npmjs.com/package/@reach/popover would be more appropriate for this use case?

Copy link
Contributor

Choose a reason for hiding this comment

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

This one doesn't appear on their website πŸ€”

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, not sure why that is exactly. I do think it's the right component for this use though. It looks like they use it as part of their combobox component.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah nice - yeah support for the position prop could definitely help clean things up a little and make this feel a little less like a re-implementation of Tippy. Hopefully it'll resolve the annoying useRect issue I was running into as well. Will give it a shot πŸ‘

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@BRKalow I gave @reach/popover a shot - did up needing some workarounds, but now have both DialogTooltip and PopoverTooltip with identical APIs which we can choose from. A quick summary:

  • DialogTooltip uses @reach/dialog, which is well-documented, and aligns somewhat with reach.tech's recommendation to use dialog for "interactive tooltips". By using dialog, we get focus behaviour, keyboard and on-click-outside functionality to dismiss the dialog by default. But, the implementation required significant workarounds, including custom positioning, and a weird effect to force re-rendering whenever the dialog is opened or closed.

  • PopoverTooltip uses @reach/popover. It seems to have no official documentation - unclear why this is. By using popover, we get focus behaviour and custom positioning by default. But, the implementation required the use of onClickOutside and onFocusOutside to allow the popover to be dismissed as expected when a visitor clicks or focuses away.

Leaning towards using @reach/popover as you recommended, wanted to run this by just in case. If that sounds good, will just commit the deletion of the dialog-based implementation and this should be good to go πŸ‘ πŸ™‡

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for spiking this out. The popover implementation seems potentially cleaner...what do you think? It seems better to have to handle the click/focus outside than the custom positioning. I'll let you make the final call here though!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

+1 I think popover is a bit cleaner - will go with that unless @jescalan has any strong objections πŸ‘

Copy link
Contributor

@jescalan jescalan left a comment

Choose a reason for hiding this comment

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

I'm curious about the popover element bryce mentioned - this is GOOD, as expected, but just feels like a lot of manual implementation

.changeset/smart-countries-fly.md Outdated Show resolved Hide resolved
- πŸ”¨ Accessibility improvements
- grid items with dialog tooltips now render as focus-able `button` elements
- tooltip a11y is now improved, with features such as focus lock and auto-focusing of the close button when the dialog is opened, thanks to ReachUI
- πŸ”¨ Converts to Typescript
Copy link
Contributor

Choose a reason for hiding this comment

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

πŸŽ‰

[s.removeBorders]: removeBorders,
})}
>
{hasLink ? (
Copy link
Contributor

Choose a reason for hiding this comment

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

Only idea I have would be to make it a separate function component, then you could use normal if/else statements. But this doesn't really feel that bad honestly.

@@ -0,0 +1,195 @@
import React, { useState, useRef, useEffect } from 'react'
import { DialogOverlay, DialogContent } from '@reach/dialog'
Copy link
Contributor

Choose a reason for hiding this comment

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

This one doesn't appear on their website πŸ€”

/* We don't want scroll lock, as this is more of a tooltip than a dialog.
Due to our tooltip-like positioning, when scroll lock is enabled, the
tooltip can become locked in an offscreen position. */
dangerouslyBypassScrollLock={true}
Copy link
Contributor

Choose a reason for hiding this comment

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

agreed and this seems ok to me in this instance

/>
</Portal>
)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

My only thoughts here are that this is a lot of complicated custom code - I'm not sure that its avoidable, it almost feels like we removed tippy only to effectively re-implement it manually

@zchsh zchsh changed the title feat(logo-grid): convert to CSS modules, except Tippy feat(logo-grid): convert to CSS modules Sep 3, 2021
@zchsh
Copy link
Contributor Author

zchsh commented Sep 8, 2021

@BRKalow @jescalan Last CSS modules component conversion to merge πŸŽ‰

Have switched in @reach/popover here as discussed earlier - details in this comment. Open to thoughts here. I'll interpret PR approval as a signal we should go with the popover implementation and drop the dialog one πŸ‘

Copy link
Contributor

@BRKalow BRKalow left a comment

Choose a reason for hiding this comment

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

Happy with Dialog or Popover, though the popover implementation seems a little cleaner to me!

Copy link
Contributor

@jescalan jescalan left a comment

Choose a reason for hiding this comment

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

I'm good with whichever option you both prefer, which sounds like popover. A little uncomfortable with the amount of custom code involved with both, but it's probably better than tippy, right?

@zchsh
Copy link
Contributor Author

zchsh commented Sep 8, 2021

@jescalan Yep, I think it's still more lightweight than Tippy. The "tooltip arrow" part feels like a decent amount of custom code here... I expect if we continue to see tooltip patterns in design (which seems very likely) we can refine things further, and hopefully make the custom parts feel a little more contained πŸ‘

@zchsh zchsh merged commit 73927a8 into main Sep 8, 2021
@zchsh zchsh deleted the zs.logo-grid-css-modules branch September 8, 2021 19:16
@github-actions github-actions bot mentioned this pull request Sep 8, 2021
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

3 participants