-
Notifications
You must be signed in to change notification settings - Fork 12
Code review #4
Description
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 thibaudcolas#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 wagtail/wagtail#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.
- Make an API for the package instead of doing the Wagtail integration in it (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment), Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment))
- Overly generic class names across all of the UI (e.g.
icon
,field
,required
) – these should use BEM, and-or be namespaced (see "Styles" section below, and Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment), Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment), Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment), Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment), Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment), Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment), Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment)) - Do not use elements like
aside
,article
,header
. They provide no semantic value for a form / widget like StreamField (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment)) -
AddButton
'sgroupedBlockDefinitions
– What happens if key is an empty string? It seems like this is going to override the groups (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment)) -
extractText
– Use eithertextContent
orinnerText
depending on the desired outcome (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment)) - Add
CustomEvent
polyfill to Wagtail for IE11 support, or change the code not to need it (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment)) - Add an API for Wagtail to provide translations (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment))
- Button labels should be provided by Wagtail, so they are localised instead of english-only (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment))
- Disabled buttons in block actions should be disabled with the corresponding HTML attribute rather than CSS (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment))
- Replace deprecated
initEvent
with event constructor (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment)) - Move
StreamField
constructor side-effects tocomponentDidMount
(see react-redux section below) (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment)) - See whole "Packaging" section below
Performance
-
Revisit the AnimateHeight implementations to only render their children to the DOM when they are meant to be displayed (AddButton's panel, Block children) (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment), Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment), Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment)) - Clean up listeners / observers set up in RawHtmlFieldInput
componentDidMount
, which can cause memory leaks (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment)) - Small thing – do not render the add panel's group name if it's an empty string (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment))
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.
- Add error handling to inner script execution (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment))
- Add error handling to RawHtmlFieldInput's
componentDidMount
(Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment)) - Add error handling of
moveBlock
action (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment)) - Ideally, add error handling to all of StreamField (
componentDidCatch
?) for unforeseen sources of error
Minor ones
Minor but still sources of concern
-
Preserve script tags in inner script execution instead of usingeval
(Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment)) -
<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. (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment), Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment)) -
getIsMobile
– Usewindow.matchMedia
to make sure the JS and CSS breakpoint definitions are in sync (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment))
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.
- Stop bundling dependencies in the compiled/published code, these should be resolved as part of the dependency management and build of the consumer code (Wagtail’s) (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment))
- Publish the package as both CommonJS and ES modules, with
main
andmodule
attributes inpackage.json
(Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment)) - Remove any side effects on import, and mark the package as
"sideEffects": false,
for Webpack consumers (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment)) - Expose the compiled source (
dist
) via npm only, not in version control (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment)) - Consider using Rollup instead of Webpack to achieve all of the above easily (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment))
- Do not pin dependencies to exact versions in
package.json
. Instead, use apackage-lock.json
. (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment)) - Move non-dev dependencies to
dependencies
:classnames
,react-animate-height
,react-beautiful-dnd
,uuid
(react-redux
,redux
,redux-thunk
,react
,react-dom
,prop-types
) (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment)) - Move most fundamental and heavy dependencies to
peerDependencies
:react-redux
,redux
,redux-thunk
,react
,react-dom
,prop-types
(Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment), Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment))
Bonus points
- Consider using
sass
(official Dart implementation) to get rid of the annoying native code compilation ofnode-sass
. (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment)) - Consider configuring
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. (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment)) - Remove unused
@babel/cli
(Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment)) - Consider replacing
@babel/plugin-proposal-decorators
and decorators syntax with normal higher-order functions/components. (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment))
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.
- Document the
blockDefinitions
schema (can be as simple as adding comments toBlockDefinitionType
, and linking to this) (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment)) - Document the need for a
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 (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment)) - Document the need for the
element-closest
polyfill for IE11 support (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment)) - Document the need for the
Array#find
polyfill for IE11 support (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment)) - Document the need for the
Object#entries
polyfill for IE11 support (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment)) - Document the need for the
CustomEvent
polyfill for IE11 support (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment))
Development & demo env
- Consider setting up linting with ESLint and Stylelint, with a config similar to that of Wagtail (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment))
- Consider setting up Prettier for automated formatting (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment))
- Consider swithing to Storybook for a better dev / demo environment (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment))
- Consider removing
drop_console
setting from UglifyJS setup (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment)) - Consider using SVG instead of FontAwesome for the icons (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment))
- Add Autoprefixer to the CSS build (Wagtail already has it, so this is just about having a similar setup for this package’s dev/demo site, so it can be used for cross-browser testing) (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment))
- Consider adding an example with a custom JS widget (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment))
Styles
- Split
src/index.scss
into multiple files, ideally following the separation between React components (one styles file per component) (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment)) - Use BEM notation for element selectors, e.g.
.add-block-button
instead ofbutton.add
(Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment), Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment)) - Use BEM notation for state selectors, e.g.
.children-container--dragging
instead of.children-container.is-dragging
(Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment), Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment), Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment)) - Replace bare element names by class names, e.g.
.block-header
instead ofheader
(Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment), Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment)) -
Consider using a JS-added--focus
class instead of:focus-within
, if the functionality is important enough (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment))
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 (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment)) - All other icons should also have text-only labels for screen reader users (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment))
- All icons should be marked as
aria-hidden="true"
so screen readers ignore them (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment), Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment)) - All icons should be built with SVG instead of an icon font (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment))
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:
[...] there is a behavior change around dispatching actions in
constructors
/componentWillMount
. Previously, dispatching in a parent component's constructor would cause its children to immediately use the updated state as they mounted, because each component read from the store individually. In version 6, all components read the same current store state value from context, which means the tree will be consistent and not have "tearing". This is an improvement overall, but there may be applications that relied on the existing behavior.
This is the problematic code:
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 to componentDidMount
would make the problem even more obvious.
I can see a few solutions:
- Move the
initializeStreamField
logic out ofStreamField
, to be done when the store is created - Move
initializeStreamField
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:
- react-beautiful-dnd also uses Redux and react-redux v5, but doesn't declare them as
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. - Wagtail uses v5, and switching to v6 will require updating Wagtail's code. Shouldn't be a big deal, but it will take a bit of time.
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
or React.memo
to still have the same rendering performance)
- Remove connection of
BlockContent
, and use props fromBlock
instead (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment)) - Remove connection of
BlockHeader
, and use props fromBlock
instead (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment)) - Remove connection of
BlockActions
, and use props fromBlock
instead (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment)) - Remove connection of
RawHtmlFieldInput
, and use props fromFieldInput
instead (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment))
This would also make it easier to write tests for those components, which I would really like to see.
Smaller JS / React things
- Consider using updater callbacks for
setState
calls such asthis.setState({open: !this.state.open});
(Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment)) - Consider binding the
addHandler
's key to the function, instead of storing it and reading it in HTML. (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment)) - It's more idiomatic to check for null/undefined ("falsey") values by doing
if (value)
instead ofif (value === null)
,value !== undefined
(Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment), Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment), Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment)) - There are other idiomatic JS comments I've added along the way.
- Consider using the latest syntax for React fragments (
<></>
) (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment)) -
Remove unneeded boolean parameter ofgetDescendantsIds
(always called withtrue
) (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment)) - Split
getNewBlock
of the utils into smaller functions (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment)) - Remove unneeded prefixed fallbacks for
MutationObserver
(Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment)) - Do not use
h3
for BlockHeader's icon (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment)) - Extract block title rendering logic, and add tests for it (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment))
- Use a switch statement for the reducer (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment))
- Rename
actions.js
toreducers.js
(Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment)) - Unneeded deepCopy? (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment))
- Use FSA for actions like Wagtail (Code review for upcoming Wagtail integration thibaudcolas/react-streamfield#1 (comment))