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

[HOLD for payment 2023-06-01] [$4000] Paste H1(# test) message in chat causes incorrect line breaks #16921

Closed
2 of 6 tasks
kavimuru opened this issue Apr 4, 2023 · 85 comments
Closed
2 of 6 tasks
Assignees
Labels
Awaiting Payment Auto-added when associated PR is deployed to production Bug Something is broken. Auto assigns a BugZero manager. Daily KSv2 Internal Requires API changes or must be handled by Expensify staff

Comments

@kavimuru
Copy link

kavimuru commented Apr 4, 2023

If you haven’t already, check out our contributing guidelines for onboarding and email contributors@expensify.com to request to join our Slack channel!


Action Performed:

  1. Navigate to chat in Chrome and Safari
  2. Open an existing chat and compose a H1 message (e.g., # hello world)
  3. Send the message
  4. Hover over the message and click on the edit pencil
  5. Edit the H1 message (e.g., # hello world1234)
  6. Click Save Changes
  7. Hover over the edited message
  8. Triple-click on the edited H1 message to highlight it
  9. Copy/paste in the composer
  10. Observe the pasted H1 message is formatted with differently

Expected Result:

Copying H1 message should not add a new line after #, and it should be the same as the original message

Actual Result:

Copying H1 message using triple click is adding a new line after #

Workaround:

unknown

Platforms:

Which of our officially supported platforms is this issue occurring on?

  • Android / native
  • Android / Chrome
  • iOS / native
  • iOS / Safari
  • MacOS / Chrome / Safari
  • MacOS / Desktop

Version Number: 1.2.94-0
Reproducible in staging?: y
Reproducible in production?: y
If this was caught during regression testing, add the test name, ID and link from TestRail:
Email or phone of affected tester (no customers):
Logs: https://stackoverflow.com/c/expensify/questions/4856
Notes/Photos/Videos: Any additional supporting documentation

Recording.131.mp4
h1-tag-bug.mov

Expensify/Expensify Issue URL:
Issue reported by: @jayeshmangwani
Slack conversation: https://expensify.slack.com/archives/C049HHMV9SM/p1680592375624629

View all open jobs on GitHub

Upwork Automation - Do Not Edit
  • Upwork Job URL: https://www.upwork.com/jobs/~015e2698490538f13d
  • Upwork Job ID: 1643345155600601088
  • Last Price Increase: 2023-05-02
@kavimuru kavimuru added Daily KSv2 Bug Something is broken. Auto assigns a BugZero manager. labels Apr 4, 2023
@MelvinBot
Copy link

Triggered auto assignment to @Christinadobrzyn (Bug), see https://stackoverflow.com/c/expensify/questions/14418 for more details.

@MelvinBot
Copy link

MelvinBot commented Apr 4, 2023

Bug0 Triage Checklist (Main S/O)

  • This "bug" occurs on a supported platform (ensure Platforms in OP are ✅)
  • This bug is not a duplicate report (check E/App issues and #expensify-bugs)
    • If it is, comment with a link to the original report, close the issue and add any novel details to the original issue instead
  • This bug is reproducible using the reproduction steps in the OP. S/O
    • If the reproduction steps are clear and you're unable to reproduce the bug, check with the reporter and QA first, then close the issue.
    • If the reproduction steps aren't clear and you determine the correct steps, please update the OP.
  • This issue is filled out as thoroughly and clearly as possible
    • Pay special attention to the title, results, platforms where the bug occurs, and if the bug happens on staging/production.
  • I have reviewed and subscribed to the linked Slack conversation to ensure Slack/Github stay in sync

@Christinadobrzyn Christinadobrzyn changed the title Copying H1(# test) message using triple click is adding a new line behind # Paste H1(# test) message in chat causes incorrect line breaks Apr 4, 2023
@Christinadobrzyn
Copy link
Contributor

I can replicate this and I think it's good to go external. Adding External label

@Christinadobrzyn Christinadobrzyn added the External Added to denote the issue can be worked on by a contributor label Apr 4, 2023
@melvin-bot melvin-bot bot changed the title Paste H1(# test) message in chat causes incorrect line breaks [$1000] Paste H1(# test) message in chat causes incorrect line breaks Apr 4, 2023
@MelvinBot
Copy link

Job added to Upwork: https://www.upwork.com/jobs/~015e2698490538f13d

@MelvinBot
Copy link

Current assignee @Christinadobrzyn is eligible for the External assigner, not assigning anyone new.

@MelvinBot
Copy link

Triggered auto assignment to Contributor-plus team member for initial proposal review - @0xmiroslav (External)

@melvin-bot melvin-bot bot added the Help Wanted Apply this label when an issue is open to proposals by contributors label Apr 4, 2023
@MelvinBot
Copy link

Triggered auto assignment to @NikkiWines (External), see https://stackoverflow.com/c/expensify/questions/7972 for more details.

@alexxxwork
Copy link
Contributor

alexxxwork commented Apr 4, 2023

Proposal

Please re-state the problem that we are trying to solve in this issue.

When we copy h1 text manually (Ctrl+C) and paste it to composer, the formatting/markdown is lost.

What is the root cause of that problem?

The problem is similar to #16525 but not the same.
We also get HTML in copied string

const getCurrentSelection = () => {
const domRepresentation = parseDocument(getHTMLOfSelection());
domRepresentation.children = _.map(domRepresentation.children, replaceNodes);
const newHtml = render(domRepresentation);
return newHtml || '';
};

But in this case we use BaseHTMLEngineProvider and provide custom styling so it generates <h1> with a <div> inside
<TRenderEngineProvider
customHTMLElementModels={customHTMLElementModels}
baseStyle={styles.webViewStyles.baseFontStyle}
tagsStyles={styles.webViewStyles.tagStyles}
enableCSSInlineProcessing={false}
systemFonts={_.values(fontFamily)}
fallbackFonts={fallbackFonts}
>

When we use Ctrl-C CopySelectionHelper calls Clipboard.setString(parser.htmlToMarkdown(selection)); and it uses rules in ExpensiMark to generate Markdown from HTML. It ends up generating [block] instead of <div> which later generates additional \n in replace rules.

copySelectionToClipboard() {
const selection = SelectionScraper.getCurrentSelection();
if (!selection) {
return;
}
const parser = new ExpensiMark();
if (!Clipboard.canSetHtml()) {
Clipboard.setString(parser.htmlToMarkdown(selection));
return;
}
Clipboard.setHtml(selection, Str.htmlDecode(parser.htmlToText(selection)));
}

https://github.com/Expensify/expensify-common/blob/9cf047b9741d3c02820d515dccb9e0a45b6595f5/lib/ExpensiMark.js#L524

What changes do you think we should make in order to solve the problem?

To avoid this we should get rid of additional empty <div> in <h1> tag. To do so we could add a pre option in 'heading1' rule of htmlToMarkdownRules in Expensimark:
https://github.com/Expensify/expensify-common/blob/9cf047b9741d3c02820d515dccb9e0a45b6595f5/lib/ExpensiMark.js#L231-L235

pre: inputString => inputString.replace(/<(h1)><div>(.*)<\/div>(<\/\1>)/gi,'<$1>$2$3'),

@Arslaan0124
Copy link

There's more to it. If you skip the edit part and just copy the message, it copies the emojis too.
Here's a loom: https://www.loom.com/share/43b14497131c4e5f9e76c033d005038b

@NikkiWines
Copy link
Contributor

@Arslaan0124 the issue with emojis is a known bug and is being handled in this issue

@tienifr
Copy link
Contributor

tienifr commented Apr 9, 2023

Proposal

Please re-state the problem that we are trying to solve in this issue.

Copying H1 message using triple click is adding a new line after #

What is the root cause of that problem?

When we triple click and Ctrl C, the text that's copied is this <comment><h1><div>hello world</div></h1></comment> (I'm ignoring the copying of the emojis here since it's part of another issue as mentioned here)

Why is there the extra div while we're only copying the h1? It's because the BaseHTMLEngineProvider that we use will add the div for styling purpose

const BaseHTMLEngineProvider = (props) => {
.

The div will subsequently be converted to a new line, which leads to this issue.

So the root cause here is the extra div that's only for styling purpose, but is still added to the copied html.

What changes do you think we should make in order to solve the problem?

We should ignore the div that's used for styling purpose when we copy the html.

The div that's relevant to us will have the data-testid which indicates which html element it's supposed to represent (for example strong, blockquote, h1), or the div is used to group many html elements, in which case it will have more than 1 child.

So we can ignore that div that:

  • Has only 1 child
  • Does not have data-testid

It turns out we're already doing very close to that here

} else if (dom.name === 'div' && dom.children.length === 1 && dom.children[0].type !== 'text') {
, but we're keeping div that has 1 single text child, which leads to this issue.

To fix this we just have to remove the && dom.children[0].type !== 'text' in the condition

} else if (dom.name === 'div' && dom.children.length === 1 && dom.children[0].type !== 'text') {
.

Updates from here
I've tested all possible markdown cases that we have in the app and there's no case of a div being used to represent a new line, and the approach of removing the div doesn't cause any regression. The cases that I've tested are: heading, inline quote, code block, bold, italic, strike-through, normal text, normal multiline text, link, email, auto link, auto email, empty line.

Feel free to double check thoroughly on your end.

My take is: Any text-wrapping div/HTML element that is relevant to us will have the data-testid that represents the elements (h1, a, em, strong, ...). If it doesn't have it, it's created by react-native-render-html for styling purpose and should be stripped.

If we want to limit that logic to inside our markdown tags only (the tags that have data-testid) we can also do that by passing a new field to the replaceNodes to indicate we're replacing inside our markdown tags. But it might not be necessary now since there's no such case.

What alternative solutions did you explore? (Optional)

  • FYI The solution to replace the additional div in the htmlToMarkdown step will only fix the issue with the h1 tag, the bug with the div might happen for other tags as well. Also if we copy the html to other non-Expensify places then the redundant div will potentially cause other issues, I think we should keep in the Clipboard only the tags that are relevant.

@melvin-bot melvin-bot bot added the Overdue label Apr 9, 2023
@MelvinBot
Copy link

@NikkiWines, @Christinadobrzyn, @0xmiroslav Eep! 4 days overdue now. Issues have feelings too...

@Christinadobrzyn
Copy link
Contributor

I think we're still collecting proposals

@melvin-bot melvin-bot bot removed the Overdue label Apr 11, 2023
@MelvinBot
Copy link

📣 It's been a week! Do we have any satisfactory proposals yet? Do we need to adjust the bounty for this issue? 💸

@Christinadobrzyn
Copy link
Contributor

Hey @0xmiroslav would you be able to review the provided proposals to see if they will work or if we need to seek others? Thanks!

@melvin-bot melvin-bot bot added the Overdue label Apr 17, 2023
@Christinadobrzyn
Copy link
Contributor

@0xmiroslav just checking in to see if you have an update on the proposal review!

@melvin-bot melvin-bot bot removed the Overdue label Apr 17, 2023
@0xmiros
Copy link
Contributor

0xmiros commented Apr 17, 2023

Proposals are in review. Will post update today

@tienifr
Copy link
Contributor

tienifr commented May 16, 2023

@rzvc I don’t think having ‘div’ inside ‘h1’ is a common (or recommended) case in other web pages, and we shouldn’t have to patch our code based on other sites’ malformed HTML

@rzvc
Copy link

rzvc commented May 16, 2023

@0xmiroslav That is not the case. The proof of concept was a way for me to understand and explore the problem, the actual solution would be/look a lot simpler. (edit: maybe not a lot, IDK, didn't write it yet)

Also, I don't think it's particularly complex even as it is, it's an "unwrapping" implementation, which is something that's often necessary when simplifying HTML.

@tienifr DIVs inside H1s are perfectly valid HTML, and also used quite often (to convey subtext usually). So given it's standard HTML, I don't think the expectation of handling VALID input from other sources is outlandish. Choosing to modify existing HTML at copy time, in order to hide this issue is questionable at best tho.

You can look at this issue from any angle you want, but my solution is still the only correct one. As I said tho, it's not my decision, but I felt compelled to point out what it's otherwise obvious to me.

@tienifr
Copy link
Contributor

tienifr commented May 16, 2023

@rzvc please see this thread. div inside h1 is poor web design https://www.quora.com/Is-the-usage-of-DIV-within-an-H1-tag-bad-for-SEO-If-yes-why. Although it won’t throw an error, it’s bad practice from a HTML standpoint , so I doubt anyone would intentionally design the HTML that way, unless it’s a constraint of a library/other issues.

@rzvc
Copy link

rzvc commented May 16, 2023

@tienifr so then you agree that it's not malformed as you claimed, and in fact standard.

@tienifr
Copy link
Contributor

tienifr commented May 16, 2023

@NikkiWines if you and @0xmiroslav are aligned here, shall I start with the PR or wait until assignment?

Thanks!

@NikkiWines
Copy link
Contributor

Oops, yes sorry - meant to assign you earlier @tienifr. Updated!

@Christinadobrzyn
Copy link
Contributor

Hired in Upwork - External job posting here - https://www.upwork.com/jobs/~015e2698490538f13d

@melvin-bot
Copy link

melvin-bot bot commented May 23, 2023

@NikkiWines, @Christinadobrzyn, @0xmiroslav, @tienifr Whoops! This issue is 2 days overdue. Let's get this updated quick!

@Christinadobrzyn
Copy link
Contributor

working on the PR - #19030

@melvin-bot melvin-bot bot added Weekly KSv2 Awaiting Payment Auto-added when associated PR is deployed to production and removed Daily KSv2 labels May 25, 2023
@melvin-bot melvin-bot bot changed the title [$4000] Paste H1(# test) message in chat causes incorrect line breaks [HOLD for payment 2023-06-01] [$4000] Paste H1(# test) message in chat causes incorrect line breaks May 25, 2023
@melvin-bot
Copy link

melvin-bot bot commented May 25, 2023

Reviewing label has been removed, please complete the "BugZero Checklist".

@melvin-bot melvin-bot bot removed the Reviewing Has a PR in review label May 25, 2023
@melvin-bot
Copy link

melvin-bot bot commented May 25, 2023

The solution for this issue has been 🚀 deployed to production 🚀 in version 1.3.17-5 and is now subject to a 7-day regression period 📆. Here is the list of pull requests that resolve this issue:

If no regressions arise, payment will be issued on 2023-06-01. 🎊

After the hold period is over and BZ checklist items are completed, please complete any of the applicable payments for this issue, and check them off once done.

  • External issue reporter
  • Contributor that fixed the issue
  • Contributor+ that helped on the issue and/or PR

As a reminder, here are the bonuses/penalties that should be applied for any External issue:

  • Merged PR within 3 business days of assignment - 50% bonus
  • Merged PR more than 9 business days after assignment - 50% penalty

@melvin-bot
Copy link

melvin-bot bot commented May 25, 2023

BugZero Checklist: The PR fixing this issue has been merged! The following checklist (instructions) will need to be completed before the issue can be closed:

  • [@0xmiroslav] The PR that introduced the bug has been identified. Link to the PR:
  • [@0xmiroslav] The offending PR has been commented on, pointing out the bug it caused and why, so the author and reviewers can learn from the mistake. Link to comment:
  • [@0xmiroslav] A discussion in #expensify-bugs has been started about whether any other steps should be taken (e.g. updating the PR review checklist) in order to catch this type of bug sooner. Link to discussion:
  • [@0xmiroslav] Determine if we should create a regression test for this bug.
  • [@0xmiroslav] If we decide to create a regression test for the bug, please propose the regression test steps to ensure the same bug will not reach production again.
  • [@Christinadobrzyn] Link the GH issue for creating/updating the regression test once above steps have been agreed upon: https://github.com/Expensify/Expensify/issues/288384

@melvin-bot melvin-bot bot added Daily KSv2 and removed Weekly KSv2 labels May 31, 2023
@Christinadobrzyn
Copy link
Contributor

No regressions so it looks like we're at pay day! 🎉
Speed bonus applies - PR created May 16th and merged May 18th.

Paid jobs in upwork - #16921 (comment)
Closed Upwork job

@0xmiroslav can you let me know if you think a regression test is needed for this? I assume yes?

@0xmiros
Copy link
Contributor

0xmiros commented Jun 1, 2023

It's tough to find out offending PR which caused regression. I think the issue always existed after implement copy selection scrapper and heading1 markdown.

Regression Test Proposal

  1. Open any chat room on web/desktop
  2. Send new H1 message (i.e. # hello world)
  3. Edit this message keeping H1 style (i.e. # hello world1234)
  4. Click Save Changes
  5. Triple-click on the edited H1 message to highlight it
  6. Copy/paste in the composer
  7. Verify that the pasted H1 message doesn't add a new line after #, and it's the same as the original message

@Christinadobrzyn
Copy link
Contributor

Thanks @0xmiroslav I'll send this over to QA and note your research on the offending PR just in case they need anything else.

Closing this out!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Awaiting Payment Auto-added when associated PR is deployed to production Bug Something is broken. Auto assigns a BugZero manager. Daily KSv2 Internal Requires API changes or must be handled by Expensify staff
Projects
None yet
Development

No branches or pull requests