Skip to content

feat(code-block): add wrapCode option #991

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

Merged
merged 6 commits into from
Aug 17, 2023
Merged

Conversation

zchsh
Copy link
Contributor

@zchsh zchsh commented Aug 3, 2023 β€’

🎟️ Asana Task
πŸ” Preview Link


This PR updates the code-block to add support for a wrapCode boolean option.

When enabled, the wrapCode option causes long lines of code within a code block to wrap to new lines, rather than create horizontal overflow within the code block.

🎨 Screenshots

Default, overflowing options.wrapCode={true}
overflow wrap

πŸ’» Demo

2023-08-14-wrap-code-demo.mp4

πŸ§ͺ Testing

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.
    • I think snapshot tests would be most appropriate to validate the contents of this PR. Maybe one day Swingset will support them, or maybe we could explore other tooling to make this possible! In the past I think we had a setup with Storybook & Percy running πŸ’­
  • Conduct reasonable cross browser testing for both compatibility and responsive behaviour (We have a Sauce Labs account for this, if you don't have access, just ask!).
    • I don't have access to Sauce Labs, but I've tested this work in Arc, Chrome, Firefox, and Safari.
  • 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.

Sorry, something went wrong.

@changeset-bot
Copy link

changeset-bot bot commented Aug 3, 2023 β€’

πŸ¦‹ Changeset detected

Latest commit: d414d01

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-code-block Minor

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 3, 2023 β€’

The latest updates on your projects. Learn more about Vercel for Git β†—οΈŽ

Name Status Preview Comments Updated (UTC)
react-components βœ… Ready (Inspect) Visit Preview πŸ’¬ Add feedback Aug 9, 2023 0:22am

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
@zchsh zchsh added the release:canary Triggers a canary release for commits to this pull request label Aug 3, 2023
@github-actions
Copy link
Contributor

github-actions bot commented Aug 3, 2023 β€’

πŸ“¦ Canary Packages Published

Latest commit: d414d01

Published 1 packages

@hashicorp/react-code-block@6.4.0-canary-20230809002333

npm install @hashicorp/react-code-block@canary

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
code={`A line that goes on for a very long time so that it overflows the container in which it is located, which might be a pretty wide container.\nThis is a second line of code.\nAnd a third line.\nAnd another line, this is the fourth line.`}
/>

#### Wrap Code
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added a section to docs.mdx to demo the new behaviour.

highlight?: boolean
highlight?: string
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 converted the code-lines partial to .tsx in this process, this is a type error unrelated to code wrapping that came up.

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 file is likely easier to view in "side by side" mode, as the corresponding JSX has been significantly refactored.

// When the floating copy button is present, we add padding to many lines
const padRight = Boolean(hasFloatingCopyButton)

if (wrapCode) {
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 refactored this component fairly significantly, which felt necessary in order to make the wrapCode option feasible.

For some context, there are fundamentally different layout needs with wrapCode compared to without:

  • When allowing overflow, we want the lines of code to scroll together, but we want the line numbers for each line to stay in a fixed position, like a frozen spreadsheet column. This necessitates a two-column layout at the top level, with the line numbers in one column, and the line contents in a separate adjacent column with scrollable content.
  • When wrapping code, a single line will take up more vertical space when it wraps. The two-column layout needed for overflowing code is no longer usable - the only approach I can imagine would be to somehow detect wrapping in each line in the "lines" columns and constantly update the corresponding line number contents in the "numbers" column to contain an identical number of newlines. It seemed much easier to rethink this use case as an alternate layout of the same atomic components, with a single column where each "row" is a flex layout that contains both the line number and the line contents.

To achieve these varied layouts, the previous CodeLines component was first refactored into smaller composable components, and then here in this conditional, we compose those smaller components into the appropriate layouts depending on the incoming wrapCode boolean.

@zchsh zchsh marked this pull request as ready for review August 14, 2023 18:43
@zchsh zchsh requested review from a team and dstaley August 14, 2023 18:43
@zchsh
Copy link
Contributor Author

zchsh commented Aug 14, 2023

@dstaley Tagged ya here since while this PR and the wrapCode option it introduces is currently for a dev-dot use case, this'll obviously show up on a lot of other sites as well - would love another set of eyes on it πŸ™‡β€β™‚οΈ

Copy link
Contributor

@dstaley dstaley 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 definitely a tricky problem, but I love that your solution is clear and thoroughly documented! Let two non-blocking comments.

import s from './code-lines.module.css'

interface CodeLinesProps {
code: string | ReactElement
Copy link
Contributor

Choose a reason for hiding this comment

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

Just curious: is there a specific reason you went with ReactElement? I think code: ReactNode would be the more accurate type since that's inclusive of string.

Copy link
Contributor Author

@zchsh zchsh Aug 17, 2023 β€’

Choose a reason for hiding this comment

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

Great point! Intent was that we might want string | ReactElement as it gets a little more specific about code not being any of the other possible types under ReactNode:

type ReactNode = ReactElement | string | number | ReactFragment | ReactPortal | boolean | null | undefined;

Open to changing it if that doesn't feel quite right though πŸ™‡β€β™‚οΈ

wrapCode,
}: CodeLinesProps) {
// Parse out an array of integers representing which lines to highlight
const highlightedLines = parseHighlightedLines(highlight) as number[]
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this need to be cast?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For now yes, as otherwise it seems the inferred type of highlightedLines ends up being never[], and we get a type error later at highlightedLines.indexOf(number) !== -1.

Ideally I'd have jumped in and converted parseHighlightedLines to TypeScript as well, but I didn't quite have the energy πŸ˜…

@zchsh zchsh merged commit 534b651 into main Aug 17, 2023
@zchsh zchsh deleted the zs.code-block-support-wrap branch August 17, 2023 19:24
@hashibot-web hashibot-web mentioned this pull request Aug 17, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
release:canary Triggers a canary release for commits to this pull request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants