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

Add zoom & rotate functionality to crash diagrams #1635

Open
wants to merge 37 commits into
base: nextjs
Choose a base branch
from

Conversation

frankhereford
Copy link
Member

@frankhereford frankhereford commented Dec 20, 2024

Associated issues

This PR hopes to close cityofaustin/atd-data-tech#19967.

Ask forgiveness, not permission

Here's the guiding image:

Image 394877044 924x1134

Building out from the image mentally -- I took some liberties, but have done my best to stay true to the new nextjs visual theme and aesthetic. I'm very 👍 on the VZE style and hope we perfect it.

But to be specific, here's some of the things that are big enough changes from the image to point out. I'm really leaning into only including the elements indicated by the arrows in the guiding image.

  • Recolor the button controls in the primary color like the 'Edit' button
  • Increase the size of the iconography in the buttons.
  • Replace the "do again" symbol with the "undo" symbol, but I did have to grab an icon from another font, not bootstrap. I think it matches really well.
  • Drop the redundant "Reset" button from near the rotation control. I wired up the top-right "undo" button to reset both zoom and rotation.
  • Drop the instructive label on the rotation handle. Users are repeat users for us, and any curious person will teach themselves what this harmless handle does real quick, and we can avoid the clutter.

Testing

  • Grab the deploy preview link from below in the 🤖 comment and check out some crash diagrams.
  • Do all the things, see if you can break them.
  • Consider the stylistic choices I have made.
    • Does this belong on this page?
    • Are you confused about what anything means...
      • ... after you play with it for a few seconds?
  • Visit a temp crash page to see what error messages look like. I found these crashes by loading local dev with production data.
    • Look good?
    • Are you cool that you can slide them around a little like an image, but they snap back?
      • I kinda dig how it's a harmless fidget toy in a way, and it's kinda hard to disable that functionality, I think. I do hide the image manipulation buttons though.

PS: DX experience

I absolutely felt a pang of joy when I could click a link in the CI PR comment and get a build log from Netlify that clearly indicated what the build problem was, like only typescript will do. It was so easy to find and fix where I went sideways. Typescript rocks! 🐐


Ship list

  • Code reviewed
  • Product manager approved

@frankhereford frankhereford self-assigned this Dec 20, 2024
Copy link

netlify bot commented Dec 20, 2024

Deploy Preview for vision-zero-nextjs ready!

Name Link
🔨 Latest commit 22fef34
🔍 Latest deploy log https://app.netlify.com/sites/vision-zero-nextjs/deploys/6769883cb87454000816ea6a
😎 Deploy Preview https://deploy-preview-1635--vision-zero-nextjs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

@frankhereford frankhereford changed the title Frank/diagram gizmos Add zoom & rotate functionality to crash diagrams Dec 20, 2024
Copy link
Member

@chiaberry chiaberry left a comment

Choose a reason for hiding this comment

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

My comments are about the UX and not the code, the code is 👍🏼

I was playing with it in existing prod and noticed that the reset and undo buttons do two different things, resetting the rotation and the undo button resets the zoom. I do not know if users would like to be able to keep that functionality separate. Like if you rotated a specific way, zoomed out and then wanted to keep the rotation but reset the zoom.

Speaking of zooming out, I can't zoom out on the preview link unless ive already zoomed in. I can zoom out on prod.

The issue doesn't explicitly say to include the download cr3 button in this same card, I went to look for it on the ACC figma designs and I couldnt find it at first. Because they changed it into a little save icon at the bottom, which at first glance I don't like. They also combine the reset buttons into one like you did Frank, so maybe my desire for more fine tuned controls isn't what our users want anyway.

@frankhereford
Copy link
Member Author

@chiaberry wrote:

I went to look for it on the ACC figma designs

I thought about trying to find them, but I didn't know where to start on that, and the issue itself doesn't mention using them -- it doesn't mention anything. So I fell back to doing just what the issue said (3 arrows), and I liked where it was going, so I stuck with that.

@frankhereford
Copy link
Member Author

@chiaberry wrote:

Speaking of zooming out, I can't zoom out on the preview link unless ive already zoomed in. I can zoom out on prod.

I spent a good little chunk of time working on the image sizing, mainly focusing on making sure the entire image would be visible on first load, and by the nature of that, it's at its most meaningful "zoomed out" state on load. I think being able to zoom out to make the image smaller and less detailed than what it shows on-load an anti-feature. Just my two cents; I can probably figure out a way to make it zoom-out-able by starting with it zoomed in at 200% and then using CSS to scale that down to fit in the element -- something like that. Seems counter productive to me though.

Copy link
Member

@johnclary johnclary left a comment

Choose a reason for hiding this comment

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

Frank—it's so awesome to have this feature come to life. It works like a charm. And it's awesome to have you shipping on VZE!!

Consider the stylistic choices I have made.

They're excellent—thanks for the subtle UX improvements — this looks sharp and intuitive.

Are you cool that you can slide them around a little like an image, but they snap back?

Yep—totally fine!

One minor detail is that I'm getting a vertical scrollbar inside the card body. Can you track down the source of that unwanted pixel push?

Screenshot 2024-12-20 at 12 13 45 PM

The rest of my feedback is really just asking you to reach for the bootstrap utility classes instead of hardcoded styles. Thanks again! 🙏

app/components/CrashDiagramCard.tsx Outdated Show resolved Hide resolved
app/components/CrashDiagramCard.tsx Show resolved Hide resolved
app/components/CrashDiagramCard.tsx Show resolved Hide resolved
app/components/CrashDiagramCard.tsx Show resolved Hide resolved
position: "relative" as const,
top: "-1px",
fontSize: "1.3em",
};
Copy link
Member

@johnclary johnclary Dec 20, 2024

Choose a reason for hiding this comment

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

would you mind taking a look at our other icon + button components and matching their style? you should be able to get away with using zero style props here vs the utility classes—and the AlignedLabel component might be helpful too.

And if you feel like we need a generic "IconButton" component—by all means go for it.

And if we're still unhappy with look + feel without pixel pushing, I am thinking we can add these kinds of todos in https://github.com/cityofaustin/atd-data-tech/issues/20013—so that we can hopefully solve them with SCSS in a centralized file 💅

Copy link
Member Author

@frankhereford frankhereford Dec 20, 2024

Choose a reason for hiding this comment

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

584d84f & 75836cd

Thanks John -- I looked at buttons from around the app by searching for and I found plenty of examples where we had an pictograph + text, and because you mentioned AlignedLabel, I'm taking it to mean that you want some labels on these buttons. Right?

How's this look?

app/components/CrashDiagramCard.tsx Show resolved Hide resolved
app/components/CrashDiagramCard.tsx Outdated Show resolved Hide resolved
/>
)}
{diagramError && crash.is_temp_record && (
<Alert variant="info" style={{ marginTop: "50px" }}>
Copy link
Member

@johnclary johnclary Dec 20, 2024

Choose a reason for hiding this comment

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

nice—i agree that moving this alert toward the vertical center of the card is a better look, but i would prefer to use flexbox utilities to center this vertically (instead of marginTop) . we have a few uses of align-items-center if you want to check that out 👀

Copy link
Member Author

@frankhereford frankhereford Dec 20, 2024

Choose a reason for hiding this comment

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

Thanks @johnclary!

Can you clarify -- you noted that you prefer it scooted up to the top as a better look, but ask for align-items-center. The scooted-up version would be using align-items-start i think, and if i use align-items-center, it's centered at the midpoint of the vertical space.

Also, I don't think bootstrap has a utility that let's me define a custom width of 490px, so I can center properly. Any ideas?

7606df2 <= me guessing that you want align-items-start

PS: This is like my tRPC comment -- I fully dig how we're doing it, but here's a thing I loved about tailwind in t3. They have a solution for when you really need a hardcoded value, like here, you'd be able to use the class w-[490px] and it would end up making the custom width utility for you out of that. Seems like no big thing, but you get the the perks of 100% utility class styling but also can define extensions to it in-line.

)}
{diagramError && !crash.is_temp_record && (
<Alert variant="danger" style={{ marginTop: "20px" }}>
<p>The crash diagram is not available.</p>
Copy link
Member

Choose a reason for hiding this comment

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

I'm with you—shorter is sweeter. One thing I was trying to convey with the old message is that we should have 100% CR3 PDF coverage for all crashes except temp crashes or crashes that occurred 10+ years ago. And otherwise we kind of do want a user to let us know if a PDF is missing.

I guess we could over-engineer this and check the crash_timestamp to further refine these errors?

app/components/CrashDiagramCard.tsx Show resolved Hide resolved
@frankhereford
Copy link
Member Author

@johnclary wrote:

One minor detail is that I'm getting a vertical scrollbar inside the card body. Can you track down the source of that unwanted pixel push?

Good call! I had my viewport set at 110% zoom for some reason which was masking this for me. ae3eaa2

<TransformWrapper initialScale={1}>
{!diagramError && <ZoomResetControls setRotation={setRotation} />}
<TransformComponent>
<div style={{ height: "330px", width: "100%", overflow: "hidden" }}>
Copy link
Member

@johnclary johnclary Dec 20, 2024

Choose a reason for hiding this comment

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

Frank, this is looking better, but I think we can get it to a place where we're not hardcoding any div sizes at all.

Looking at the html markup, I think we can probably nix the below div completely, sine the TransformWrapper and TransformComponent are adding their own divs.

Here's a quick demo of leaning into the flex utilities for this. By making the Card.Body a flex-column, the rest of the divs should stack up without overflowing. I think you can also strip out the height/width on the Image component as well—since the fluid prop is going to let it fill the parent container.

   <Card.Body className="crash-header-card-body text-center d-flex flex-column">
        <TransformWrapper initialScale={1}>
          {!diagramError && <ZoomResetControls setRotation={setRotation} />}
          <TransformComponent>
            {!diagramError && (
              <Image
                fluid
                style={{
                  transform: `rotate(${rotation}deg)`,
                }}
                src={`${CR3_DIAGRAM_BASE_URL}/${crash.record_locator}.jpeg`}
                alt="crash diagram"
                onError={() => {
                  console.error("Error loading CR3 diagram image");
                  setDiagramError(true);
                }}
              />
            )}

The more time I have been spending with bootstrap, the more I find that flexboxes are usually going to solve most layout problems, especially for growing to fit and managing vertical and horizontal alignment. It's always a bunch of trial and error, but the bootstrap docs have some nice visual helpers.

Copy link
Contributor

@mateoclarke mateoclarke left a comment

Choose a reason for hiding this comment

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

Great stuff Frank!

I added one suggestion in a resolved discussion thread so just putting a link so you don't miss it. https://github.com/cityofaustin/vision-zero/pull/1635/files#r1899967213

I found all the scroll zoom and drag functionality useful and intuitive. 👏 👏

twist

app/components/CrashDiagramCard.tsx Show resolved Hide resolved
Copy link
Contributor

@roseeichelmann roseeichelmann left a comment

Choose a reason for hiding this comment

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

This is looking great Frank! Love seeing everyone's typescript and i learn a little from each PR i review ✨
I like your UX choices to cut down on the number of reset buttons we have, i think having just one that resets the image completely is perfect.

I noticed that when I click on crashes with an error banner, the zoom/reset tools and the words "crash diagram" will be on the screen for awhile before the banner takes over. Sometimes its for long enough that i can even move around and zoom on the words and then some buggy behavior starts happening. If you could prevent those tools and those words from popping up before the banner shows that would be awesome! I'm also getting the pesky horizontal scroll on the temporary banner when im at 80% page zoom.
Screencast from 01-06-2025 01:10:09 PM.webm

On the other banner, if you could center it horizontally that would scratch a great itch for me too heh
image

Copy link
Contributor

@roseeichelmann roseeichelmann left a comment

Choose a reason for hiding this comment

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

This is looking great Frank! Love seeing everyone's typescript and i learn a little from each PR i review ✨
I like your UX choices to cut down on the number of reset buttons we have, i think having just one that resets the image completely is perfect.

I noticed that when I click on crashes with an error banner, the zoom/reset tools and the words "crash diagram" will be on the screen for awhile before the banner takes over. Sometimes its for long enough that i can even move around and zoom on the words and then some buggy behavior starts happening. If you could prevent those tools and those words from popping up before the banner shows that would be awesome! I'm also getting the pesky horizontal scroll on the temporary banner when im at 80% page zoom.
Screencast from 01-06-2025 01:10:09 PM.webm

On the other banner, if you could center it horizontally that would scratch a great itch for me too heh
image

@roseeichelmann
Copy link
Contributor

also i noticed you added helper text to the zoom/reset buttons but i think those could be left as just symbols and if anything its the slide bar that could use some labeling, or those icons like @mateoclarke suggested look great!

@johnclary
Copy link
Member

johnclary commented Jan 7, 2025

Thanks everyone for the design feedback. @frankhereford here's a summary of where i'd like to go with this feature in scope of this PR.

  1. Replace the CSS sizing with flexbox, like I mentioned in this comment. I'm very happy to pair on this—I know these can be a bit tricky
  2. Let's go ahead and pull the Alert banners out of the part of the transform component so they don't wiggle around. So conditionally render either the transform component or the Alert banners.
  3. Remove labels from zoom buttons (sorry for my miscommunication on that!) and add a rotate icon to the card footer. Here's a little design i drew up for this:
Screenshot 2025-01-07 at 1 01 14 PM

And here are a few bits of useful JSX i used for the design.

First, the card footer, which has the FaRotate icon in primary next to the RotateControl

        <Card.Footer className="text-center">
          <div className="d-flex align-items-center">
            <div className="me-3 text-primary fs-5">
                <FaRotate />
            </div>
            <RotateControls rotation={rotation} setRotation={setRotation} />
          </div>
        </Card.Footer>

And a small change to the RotateControl itself—adding w-100 to the outer div and removing the style entirely from the Form.Range component:

    <div className="mt-2 w-100">
      <Form.Range
        min="-180"
        max="180"
        value={rotation}
        id="formControlRange"
        onChange={rotate}
        title="Rotate Diagram"
      />
    </div>

Thanks for bearing with all the feedback on this one!

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.

5 participants