-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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 CodeMirror to the HTML block #4348
Conversation
2eeb4c5
to
37445c7
Compare
Cross-posting related issue: #2768. I didn't take any measurements on the size of CodeMirror, but I would expect it isn't a small one :) I think it's fine to include it temporarily on the initial editor load. However we should at least double check what impact it has on the size of JS and CSS loaded, and mention it in the #2768 so we know if we have to act before it gets merged into the core. |
Thanks @gziolo. Some quick measurements from running
A 1.9 MB (55%) increase! 😱 |
cc'ing some people who might want to track this work:
|
@noisysocks thanks for checking 🙇 Another thing we can add as a follow-up PR is HTML linting which is built-in in the core. By the way, the code for linting is lazy-loaded when editing plugin files. Which is a very good sign since we discovered that CodeMirror adds 1.9 MB to the uncompressed build size. |
components/code-editor/test/index.js
Outdated
} ) ); | ||
|
||
const wrapper = shallow( <CodeEditor value={ '<b>wowee</b>' } /> ); | ||
expect( wrapper.find( 'textarea' ) ).toHaveLength( 1 ); |
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.
We could use snapshot testing here, too. See example:
gutenberg/components/notice/test/index.js
Line 15 in 300ca0a
expect( wrapper ).toMatchSnapshot(); |
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.
Good idea. I've changed this to a snapshot test in 17dbeb231a901007b08e5fd0dd75840bdbbc1489.
Ouch. |
17dbeb2
to
e2c619d
Compare
Any feedback, questions, or concerns with this PR? I'd like to get the basic functionality merged and then iterate on it. I've started work on lazily loading the assets over in #4500. |
Really nice work, fun to see this, it's a very very cool block. There are a couple of things we should address in this PR. First off, can we tweak the padding a little bit so it looks like this? I think this should do it:
There's a z-index issue with adjecent block toolbars: One important issue we have to fix is that you need to be able to escape this block using just the keyboard:
Question for a future PR, doesn't have to be this one — can we customize the colors of the syntax highlighting? If yes, I'd love for us to use more WordPressy colors. Alternatively, I'd love it if we could at least use something like |
Yep! It's just a matter of styling the |
e2c619d
to
7714d1e
Compare
Thanks @jasmussen! I've implemented your suggested changes.
I made it so that pressing UP/DOWN first moves the cursor to the start/end of the line. A subsequent press will then move focus to the previous/next block. It feels really natural in my testing.
I didn't implement this since pressing ENTER twice is quite common while writing code and e.g. adding whitespace between lines. |
7714d1e
to
7847ea2
Compare
sorry I'm coming in somewhat late. what did we decide about async-loading CodeMirror? are we wanting to still do that? we can use example code in #665 where I have everything async-loading including the editor and the language modes I don't think it's responsible for us to add almost 2 MB to the download, especially since we expect most page views to not open the HTML editor. I'd be happy to help with this too, though my cycles lately have been limited and stretched |
Yes, see #4500 |
Any more thoughts on this? 😊 |
I'd really like to see us wait until this is loading on-demand before merging it otherwise it's going to instantly bloat the editor in no small way - that is, I'd love to see something like #4500 merge first or have some changes here to lazy-load. |
I'll hold off on hitting the merge button until #4500 is ready. Nonetheless, I'd like to get this PR into an approved and ready-to-go state 🙂 |
Visually this is in a good place 👍: I also appreciate that the arrow at the beginning and end of the block enters adjecent blocks or creates a new paragraph so at least you can escape the block. 👍 However I'm seeing some weird behavior when testing. Not sure if it's intentional, but basic dummy text paragraphs are joined together on reload: |
It looks like this is because Gutenberg pretty prints the post HTML before saving. You can see that this happens on |
d9cbfaa
to
e51f342
Compare
5c729cc
to
35d7dde
Compare
Rebased and fixed conflicts. How do we feel about getting this in? cc. @westonruter @mtias |
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.
A couple minor things I commented on.
Also, there is a bit of an unexpected UX behavior whereby if I shift-arrow to select some text, I get the block toolbar appearing. I understand that normally makes sense because toolbar buttons like bold and italic. But since there are no such buttons in the toolbar for Custom HTML (yet, as I suppose there could be shortcuts to add common tags), it is a little distracting to see the HTML/Preview buttons flash whenever I select some text with the keyboard.
components/code-editor/README.md
Outdated
return ( | ||
<CodeEditor | ||
value={ '<p>This is some <b>HTML</b> code that will have syntax highlighting!</p>' } | ||
onChange={ value => console.log( value ) } |
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.
Attributes here have inconsistent indentation.
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.
Oops. Fixed in 87af208.
components/code-editor/README.md
Outdated
======= | ||
|
||
CodeEditor is a React component that provides the user with a code editor | ||
that has syntax highliting and linting. |
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.
s/highliting/highlighting/
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.
Oops. Fixed in 87af208.
this.lastCursor = editor.getCursor(); | ||
} | ||
|
||
onKeyHandled( editor, name, event ) { |
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 love this.
35d7dde
to
87af208
Compare
Introduces a new <CodeEditor> component which wraps a CodeMirror enhanced <textarea>. This component is then used to provide syntax highliting in the Custom HTML block.
This prevents the cursor from moving around to different blocks while editing code.
Make the CodeEditor in the HTML block look vaguely like what Joen mocked up in #1386.
Prevent the iframe from capturing pointer events and preventing the user from being able to select the block.
`componentDidUpdate` and `componentDidMount` fire after DOM reconcilation but before the frame is painted. CodeMirror's `focus()` method seems to only work *after* paint. Scheduling `focus()` to happen after the next frame is painted seems to be the best that we can do.
We only care that the component renders without an error and doesn't change unintentionally, which makes this a perfect use case for snapshot testing.
Bumps up the z-index of every element that is at the same depth as the block toolbar. By making these indicies greater than 4, we ensure our contextual elements appear over the CodeMirror editor. This stops the CodeMirror gutter from appearing over the block toolbar.
- Permit pressing ESCAPE to unfocus the currently selected block. - Allow pressing UP/DOWN to move focus to the previous/next block when the cursor is at the start/end of the editor.
Wraps CodeEditor with a component that lazily loads the scripts and styles that CodeMirror needs. This lets us avoid using wp_enqueue_code_editor() which adds considerable bulk (~ 1.9 MB) to the page load.
87af208
to
30ade66
Compare
Thanks for reviewing this, @westonruter!
Good catch. I agree that it feels weird. I think to address this we need to split Since this would require moving a few things around and it's not a major issue, I'll defer this work for later. |
👀 What this is
Adds CodeMirror to the HTML block. This means that a user can type their custom HTML using a fully featured text editor with linting and syntax highlighting:
🤔 How this works
CodeMirror was added to core in WordPress 4.9 which means we can enhance a
<textarea>
simply by callingwp.codeEditor.initialize()
.To make CodeMirror easier to use within React, I introduced a
<CodeEditor>
component which acts as a drop-in replacement for a<textarea>
.I've also incorporated some of @westonruter's work in #1625 by using
SandBox
within our HTML block. This makes previewing a HTML block more robust. Specifically, you can now insert HTML that blocks rendering, e.g.:To avoiding adding a whopping 1.9 MB to our assets, I've implemented a very simple mechanism to lazily load the assets that are normally queued by calling
wp_enqueue_code_editor()
.✅ How to test
<script>document.write( 'Hey!' );</script>
<script>
tag