-
-
Notifications
You must be signed in to change notification settings - Fork 182
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
refactor(TextFormatter): create TextFormatter component TASK-996 #5257
Closed
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
56 changes: 56 additions & 0 deletions
56
jsapp/js/components/textFormatter/textFormatter.component.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import type {ReactNode} from 'react'; | ||
|
||
const regExpMatchParts = /[^*[\]]+|(\*\*[^*]*(?:\*[^*]+)*\*\*)|(\*[^*]+\*)|(\[.+?\]\(.+?\)({:.+})?)/g; | ||
|
||
const processText = (text: string): ReactNode[] => { | ||
const parts = text.match(regExpMatchParts); | ||
|
||
if (!parts) {return [text];} | ||
|
||
const formattedParts: ReactNode[] = []; | ||
|
||
for (const part of parts) { | ||
if (part.startsWith('**')) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just a style issue, but we generally like to avoid if/else chains in favor of multiple simple if statements:
|
||
formattedParts.push(<strong>{processText(part.slice(2, -2))}</strong>); | ||
} else if (part.startsWith('*')) { | ||
formattedParts.push(<em>{processText(part.slice(1, -1))}</em>); | ||
} else if (part.startsWith('[')) { | ||
const [label, link, target] = part | ||
.slice(1, -1) | ||
.split(/\]\(|\)\{:target=/); | ||
formattedParts.push( | ||
<a href={link} target={target ? target.slice(1, -1) : '_self'}> | ||
{processText(label)} | ||
</a> | ||
); | ||
} else { | ||
formattedParts.push(part); | ||
} | ||
} | ||
|
||
return formattedParts; | ||
}; | ||
|
||
interface TextFormatterProps { | ||
/** | ||
* Text will be processed and formatted with markdown-like syntax. It accepts the following: | ||
* - *<text>* for italic | ||
* - **<text>** for bold | ||
* - [text](link) for links with target _self | ||
* - [text](link){:target="_blank"} for links with target _blank or other target | ||
* - Formatting can be nested to be combined | ||
*/ | ||
text: string; | ||
} | ||
|
||
/** | ||
* This component process text and applies simple formatting rules with markdown-like syntax. | ||
* This is meant to be used with long sentences that need formatting and cannot be broken down | ||
* into smaller components duo to translation issues that broken down components would cause. | ||
* The formatting options mimics markdown syntax for italic, bold and links. | ||
* | ||
* @returns ReactNode[] | ||
*/ | ||
export default function TextFormatter({text}: TextFormatterProps) { | ||
return processText(text); | ||
} |
22 changes: 22 additions & 0 deletions
22
jsapp/js/components/textFormatter/textFormatter.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import type {Meta, StoryFn} from '@storybook/react'; | ||
import TextFormatter from './textFormatter.component'; | ||
|
||
export default { | ||
title: 'misc/TextFormatter', | ||
component: TextFormatter, | ||
argTypes: { | ||
text: { | ||
description: 'Text containing markdown-like syntax for formatting. Accepts italic, bold and links with and without target indication. Formatting can be nested to be combined.', | ||
control: 'text', | ||
}, | ||
}, | ||
} as Meta<typeof TextFormatter>; | ||
|
||
const Template: StoryFn<typeof TextFormatter> = (args) => ( | ||
<TextFormatter {...args} /> | ||
); | ||
|
||
export const Primary = Template.bind({}); | ||
Primary.args = { | ||
text: 'Formatted text with **bold** and *italic* and [link](https://www.kobotoolbox.org/pricing/){:target="_blank"}. Formatting can be **bold and *italic* combined**, [also **on** *links*](http://kobotoolbox.org).', | ||
}; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't some existing library satisfy our needs here? Like https://www.npmjs.com/package/markdown-to-jsx or https://www.npmjs.com/package/react-markdown or https://www.npmjs.com/package/marked ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've used
react-markdown
before and suggest it. I like most the ability to override all components with custom implementation, that's useful especially with Link-s.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the need here was a simple subset of what markdown does I opted not to use a full library, not at first at least, so we wouldn't have to worry about unwanted markdown styles being accidentally inserted in string and also implementing it simply would avoid having another library linked to the project, but after talking to @Akuukis about this I ended up agreeing that using a library would simplify things here, mainly because there are libraries where we can opt for which marks we want to implement.