-
-
Notifications
You must be signed in to change notification settings - Fork 144
Markdown dedent, and remove containerProps #569
Changes from 1 commit
24bdf29
ebc6856
f17e0fa
d207da3
df5192d
6eeb96a
b0f22e2
a1c36d3
f8f44c6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,6 +14,7 @@ class DashMarkdown extends Component { | |
constructor(props) { | ||
super(props); | ||
this.highlightCode = this.highlightCode.bind(this); | ||
this.dedent = this.dedent.bind(this); | ||
} | ||
|
||
componentDidMount() { | ||
|
@@ -38,6 +39,40 @@ class DashMarkdown extends Component { | |
} | ||
} | ||
|
||
dedent(text) { | ||
const lines = text.split(/\r\n|\r|\n/); | ||
let commonPrefix = null; | ||
for (const line of lines) { | ||
const preMatch = line && line.match(/^\s*(?=\S)/); | ||
if (preMatch) { | ||
const prefix = preMatch[0]; | ||
if (commonPrefix !== null) { | ||
for (let i = 0; i < commonPrefix.length; i++) { | ||
// Like Python's textwrap.dedent, we'll remove both | ||
// space and tab characters, but only if they match | ||
if (prefix[i] !== commonPrefix[i]) { | ||
commonPrefix = commonPrefix.substr(0, i); | ||
break; | ||
} | ||
} | ||
} else { | ||
commonPrefix = prefix; | ||
} | ||
|
||
if (!commonPrefix) { | ||
break; | ||
} | ||
} | ||
} | ||
|
||
const commonLen = commonPrefix ? commonPrefix.length : 0; | ||
return lines | ||
.map(line => { | ||
return line.match(/\S/) ? line.substr(commonLen) : ''; | ||
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. 👍 |
||
}) | ||
.join('\n'); | ||
} | ||
|
||
render() { | ||
const { | ||
id, | ||
|
@@ -46,11 +81,14 @@ class DashMarkdown extends Component { | |
highlight_config, | ||
loading_state, | ||
dangerously_allow_html, | ||
children, | ||
dedent, | ||
} = this.props; | ||
|
||
if (type(this.props.children) === 'Array') { | ||
this.props.children = this.props.children.join('\n'); | ||
} | ||
const textProp = | ||
type(children) === 'Array' ? children.join('\n') : children; | ||
const displayText = | ||
dedent && textProp ? this.dedent(textProp) : textProp; | ||
|
||
return ( | ||
<div | ||
|
@@ -75,7 +113,7 @@ class DashMarkdown extends Component { | |
} | ||
> | ||
<Markdown | ||
source={this.props.children} | ||
source={displayText} | ||
escapeHtml={!dangerously_allow_html} | ||
/> | ||
</div> | ||
|
@@ -111,10 +149,21 @@ DashMarkdown.propTypes = { | |
PropTypes.arrayOf(PropTypes.string), | ||
]), | ||
|
||
/** | ||
* Remove matching leading whitespace from all lines. | ||
* Lines that are empty, or contain *only* whitespace, are ignored. | ||
* Both spaces and tab characters are removed, but only if they match; | ||
* we will not convert tabs to spaces or vice versa. | ||
*/ | ||
dedent: PropTypes.bool, | ||
|
||
/** | ||
* Config options for syntax highlighting. | ||
*/ | ||
highlight_config: PropTypes.exact({ | ||
/** | ||
* Color scheme; default 'light' | ||
*/ | ||
theme: PropTypes.oneOf(['dark', 'light']), | ||
}), | ||
|
||
|
@@ -145,6 +194,7 @@ DashMarkdown.propTypes = { | |
DashMarkdown.defaultProps = { | ||
dangerously_allow_html: false, | ||
highlight_config: {}, | ||
dedent: true, | ||
}; | ||
|
||
export default DashMarkdown; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import Markdown from '../../src/components/Markdown.react.js'; | ||
import React from 'react'; | ||
import {shallow, render} from 'enzyme'; | ||
|
||
test('Input renders', () => { | ||
const md = render(<Markdown />); | ||
|
||
expect(md.html()).toBeDefined(); | ||
}); | ||
|
||
describe('dedent', () => { | ||
const md = shallow(<Markdown />).instance(); | ||
|
||
test('leading spaces and tabs are removed from a single line', () => { | ||
[ | ||
'test', | ||
' test', | ||
' test', | ||
'\t\t\ttest', | ||
' \t test', | ||
'\t \ttest', | ||
].forEach(s => { | ||
expect(md.dedent(s)).toEqual('test'); | ||
}); | ||
|
||
expect(md.dedent(' test ')).toEqual('test '); | ||
}); | ||
|
||
test('same chars are removed from multiple lines, ignoring blanks', () => { | ||
['', ' ', '\t', '\t\t', ' ', '\t \t'].forEach(pre => { | ||
expect( | ||
md.dedent( | ||
pre + | ||
'a\n' + | ||
pre + | ||
' b\r' + | ||
pre + | ||
'c\r\n' + | ||
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. Prettier certainly did not improve clarity here... and its indentation choice is peculiar for this case. Oh well... 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. cannot partially disable just for this chunk? |
||
pre + | ||
'\td\n' + | ||
'\t\n' + | ||
'\n' + | ||
pre + | ||
'e\n' + | ||
'\n' + | ||
pre + | ||
'f' | ||
) | ||
).toEqual( | ||
'a\n' + | ||
' b\n' + | ||
'c\n' + | ||
'\td\n' + | ||
'\n' + | ||
'\n' + | ||
'e\n' + | ||
'\n' + | ||
'f' | ||
); | ||
}); | ||
}); | ||
|
||
test('mismatched chars are not removed', () => { | ||
expect(md.dedent(' \ta\n\t b')).toEqual(' \ta\n\t b'); | ||
}); | ||
|
||
test('the dedent prop controls behavior', () => { | ||
const text = ' a\n b'; | ||
const mdDedented = render(<Markdown children={text} />); | ||
expect(mdDedented.find('code').length).toEqual(0); | ||
expect(mdDedented.find('p').length).toEqual(1); | ||
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. dedented: makes everything into a |
||
|
||
const mdRaw = render(<Markdown children={text} dedent={false} />); | ||
expect(mdRaw.find('code').length).toEqual(1); | ||
expect(mdRaw.find('p').length).toEqual(0); | ||
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. dedent turned off: the leading spaced mean it gets turned into a code block. |
||
}); | ||
}); |
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.
apparently not needed given our current usage, but makes me nervous to have it there unbound...