Open thibaudcolas opened 5 years ago
This PR https://github.com/noripyt/react-streamfield/pull/6 should resolve the following:
c-sf
inline with our new ITCSS inspired structure. Where sf
stands for streamfield
..c-sf-add-button vs .c-sf-add-panel
. In others there were technical reasons - .c-sf-block
in particular has a lot of children that are inter-dependant. Pulling them out into multiple files made it more complex to parse. I left some notes about this in the SCSS for that component for things we could do to improve this if need be but I don't think they should block release.is-/has-
state classes in most cases. Both methods seem to be in use across the Wagtail code base and so I went with the least effort case until we review our CSS/JS styleguide. Happy to be overridden here :)@thibaudcolas would be good to get you to confirm and provide your thoughts :)
Sweet, I'll try to have a look this weekend!
Thank you very much to both of you for these contributions!
@thibaudcolas I improved a lot of things as well after @jonnyscholes’ contribution. I updated the checkboxes above. As you can see, there is mainly one important thing remaining that I plan to do after we release the first integration to Wagtail: the error handling. I will go through the same incremental updates you did for Draftail.
I leave this review open for me to fix the remaining unticked checkboxes. But for now, this is already more than good to be in Wagtail :)
Alright! 🙂 I generally don't have the chance to review a whole package, that was a lot of fun. In the end I tried to be as exhaustive as possible with my comments, but a lot of it is fairly minor / quick to address.
I made the review as a pull request that contains all of the package's code, over at https://github.com/thibaudcolas/react-streamfield/pull/1, so it would be easier to relate comments to code. But I also summarised most of the comments below, so they are easier to relate to one another, and prioritise. It's probably easier to first read this list, then the comments in the PR.
I also made a PR to address some of the issues below (packaging, dev tools, and API), over at #5, along with the corresponding changes to Wagtail in a branch that builds upon https://github.com/wagtail/wagtail/pull/4942.
Potential problems
These are the code-level problems I would expect to cause the most pain / actual bugs. They are ordered by how important I think they are to address sooner rather than later.
icon
,field
,required
)  – these should use BEM, and-or be namespaced (see "Styles" section below, and https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249085282, https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249216525, https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249217050, https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249217966, https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249228525, https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249224192, https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249228525)aside
,article
,header
. They provide no semantic value for a form / widget like StreamField (https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249228742)AddButton
'sgroupedBlockDefinitions
– What happens if key is an empty string? It seems like this is going to override the groups (https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249217714)~extractText
– Use eithertextContent
orinnerText
depending on the desired outcome (https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249220099)CustomEvent
polyfill to Wagtail for IE11 support, or change the code not to need it (https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249221668)initEvent
with event constructor (https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249221460)StreamField
constructor side-effects tocomponentDidMount
(see react-redux section below) (https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249225067)Performance
componentDidMount
, which can cause memory leaks (https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249299793)Error handling
Generally I haven't seen much error handling code. I would expect the inner script execution to be the most problematic, since it will be very common for third-party code to break.
componentDidMount
(https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249226953)moveBlock
action (https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249222487)componentDidCatch
?) for unforeseen sources of errorMinor ones
Minor but still sources of concern
eval
(https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249226678)~<BlocksContainer />
isn't valid HTML. We shouldn't encourage people to write HTML that doesn't pass validation. For a semantic-free token/placeholder, use anoscript
tag with a data attribute. (https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249064376, https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249225428)getIsMobile
– Usewindow.matchMedia
to make sure the JS and CSS breakpoint definitions are in sync (https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249219429)Packaging - build & dependenceies
The general problem here is that the library is compiled as if it was an app, instead of a library, with all of its dependencies bundled instead of resolved by npm on install.
main
andmodule
attributes inpackage.json
(https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249068303)"sideEffects": false,
for Webpack consumers (https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249068303)dist
) via npm only, not in version control (https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249056812)package.json
. Instead, use apackage-lock.json
. (https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249069920)dependencies
:classnames
,react-animate-height
,react-beautiful-dnd
,uuid
(react-redux
,redux
,redux-thunk
,react
,react-dom
,prop-types
) (https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249072535)peerDependencies
:react-redux
,redux
,redux-thunk
,react
,react-dom
,prop-types
(https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249072842, https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249073093)Bonus points
sass
(official Dart implementation) to get rid of the annoying native code compilation ofnode-sass
. (https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249073320)transform-react-remove-prop-types
to wrap proptypes instead of removing them, as for a project like this they can be very useful in dev mode. (https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249074356)@babel/cli
(https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249068654)@babel/plugin-proposal-decorators
and decorators syntax with normal higher-order functions/components. (https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249070350)Documentation
To me this is what would be the most worthy of documentation. The
blockDefinitions
schema is probably this package’s most important API, and the polyfills are its least obvious requirements.blockDefinitions
schema (can be as simple as adding comments toBlockDefinitionType
, and linking to this) (https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249060708)position: sticky
polyfill in the project's README, and in https://docs.wagtail.io/en/v2.4/contributing/developing.html?highlight=Sticky (Wagtail doesn't come with one, it's very expensive perf-wise), for full IE11 support (https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249088987)element-closest
polyfill for IE11 support (https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249216286)Array#find
polyfill for IE11 support (https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249220422)Object#entries
polyfill for IE11 support (https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249221113)CustomEvent
polyfill for IE11 support (https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249221668)Development & demo env
drop_console
setting from UglifyJS setup (https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249077837)Styles
src/index.scss
into multiple files, ideally following the separation between React components (one styles file per component) (https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249082093).add-block-button
instead ofbutton.add
(https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249084002, https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249087414).children-container--dragging
instead of.children-container.is-dragging
(https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249084002, https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249084730, https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249085387).block-header
instead ofheader
(https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249086253, https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249087414)--focus
class instead of:focus-within
, if the functionality is important enough (https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249085971)~Accessibility
I'm sure the current StreamField implementation isn't particularly SR-friendly, so we're not aiming super high, but there are obvious improvements to be made here.
AddButton
’s + icon should have a text-only label for screen reader users (https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249217189)aria-hidden="true"
so screen readers ignore them (https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249227560, https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249227422)react-redux
react-redux recently released its v6, which uses the new React context API from React 16.4, and introduces a change in behavior that affects this package:
This is the problematic code:
https://github.com/noripyt/react-streamfield/blob/9b720dd715140f5c931560e75431ea472600f9be/src/StreamField.js#L95-L107
The consequence is that
BlocksContainer
will fail to render because it does not expect to have access to an uninitialised state. It's generally not recommended to have side-effects in the constructor anyway, moving this init tocomponentDidMount
would make the problem even more obvious.I can see a few solutions:
initializeStreamField
logic out ofStreamField
, to be done when the store is createdinitializeStreamField
call tocomponentDidMount
, and do not render theBlockContainer
until initialisation is over.Anyway, it's not necessary to upgrade to v6 now. I also have two concerns with the upgrade:
peerDependencies
. Using different versions from it would mean they get bundled twice for end users, which I would rather avoid. From memory Redux is fairly small in size, butreact-redux
is a good 20kb.Finally on the react-redux front, I'm surprised that all/most of the components in the package are connected. I would expect the performance to be better if some of the connections were removed, as they clearly duplicate their computation (but use
PureComponent
orReact.memo
to still have the same rendering performance)BlockContent
, and use props fromBlock
instead (https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249228217)BlockHeader
, and use props fromBlock
instead (https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249228076)BlockActions
, and use props fromBlock
instead (https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249228398)RawHtmlFieldInput
, and use props fromFieldInput
instead (https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249225731)This would also make it easier to write tests for those components, which I would really like to see.
Smaller JS / React things
setState
calls such asthis.setState({open: !this.state.open});
(https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249215911)addHandler
's key to the function, instead of storing it and reading it in HTML. (https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249218390)if (value)
instead ofif (value === null)
,value !== undefined
(https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249213731, https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249216741, https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249216858)<></>
) (https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249217474)getDescendantsIds
(always called withtrue
) (https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249218850)~getNewBlock
of the utils into smaller functions (https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249220662)MutationObserver
(https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249225568)h3
for BlockHeader's icon (https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249227345)actions.js
toreducers.js
(https://github.com/thibaudcolas/react-streamfield/pull/1#discussion_r249222891)