scalajs-react-material-ui
Facade for material-ui 3.1.2 in scalajs-react.
Contains code to generate a facade for each material-ui component, based on the json API description generated by react-docgen from material-ui itself, and the results of that generation.
This uses a few heuristics to assign more detailed types (mostly for functions), and to include props from base classes (e.g. Button
includes ButtonBase
props). Hopefully this will allow for relatively easy updates to new material-ui versions.
Project is in a very early state:
- Only a few components have been tested, incompletely.
- Some types are approximate, in particular
PropTypes.oneOfType
is just presented asjs.Any
. - Common React events are presented with correct event types, other event props just expect a Callback and will therefore discard event parameters (if any).
- See Todo below for more...
Building
Until this is published properly, you will need to clone the project, run sbt
, then publishLocal
. The generated facade is checked in so you don't need to run the code generation unless you want to work on the generator itself.
Code generation is not done the right way - .scala files are just generated directly into the js src folder, under package org.rebeam.mui
, by running scalajsReactMaterialUIJVM/run
from sbt.
To regenerate the muiapi.json
data describing the components, check out the material-ui project, then in the mateerial-ui project root, run react-docgen:
npx react-docgen .\packages\material-ui\src\ -o muiapi.json --pretty
You may need to trim some non-component data, for 3.1.2 this was just a reactHelpers.js
at the end of the file.
Then copy the muiapi.json
file to resources.
Notes
Material-ui of the correct version (see above) must be available for import - each component is imported individually, e.g.:
@JSImport("@material-ui/core/Snackbar", JSImport.Default)
For icons, see scalajs-react-material-icons.
Some component props are PascalCase, for example TextField
has a InputProps
prop. These have been left in PascalCase for now - they generally accept a js.Object
containing props that will be passed through to a sub-element of the component.
All components provide an optional extra prop, additionalProps
that is not found in the original material-ui component API. This can be passed a js.Object
, and each field of that object will be passed as a prop to the underlying material-ui JS component. Any fields with names matching actual documented props of the component will only be used if those props are not specified already. This emulates the spread operator in JS.
When using event props like onClick
, be careful to retrieve any required data from the event outside the Callback
itself - otherwise the event may have been reused before the Callback
runs, leading to errors. For example we might want to set some state to remember the anchor for a menu when a button is clicked:
mui.IconButton(
onClick = (e: ReactMouseEvent) => {
// Get the target here, outside the Callback
val anchor = e.currentTarget
// Now we can use the anchor value in the modState callback
scope.modState(_.copy(menuAnchor = Some(anchor)))
}
)(
icons.AccountCircle()
)
Debugging
If the code generation is incorrect, several issues can occur when attempting to use the components:
Children not detected
Some components are not correctly detected by react-docgen as having children. This results in an error like the following (for CardContent
component):
[error] found : japgolly.scalajs.react.vdom.TagMod
[error] required: japgolly.scalajs.react.vdom.html_<^.VdomNode
[error] (which expands to) japgolly.scalajs.react.vdom.VdomNode
[error] mui.CardContent()(
The fix for this is to add an entry in DocGenContext.MaterialUI
in propsIncludingInheritance
, for example:
case Component(_, "CardContent", _) => additionalPropsFrom("DOCGEN_Children")
This makes CardContent
inherit props from DOCGEN_Children
- this is a virtual component that exists just to provide this prop conveniently. If the component already inherits from another component, just add DOCGEN_Children
to the list.
Missing props
Some components are missing props because they "inherit" from another component in material-ui - this can be modelled by adding that component to DocGenContext.MaterialUI
as described above for "Children not detected". Other components simply have a missing prop in the API (e.g. a built-in react prop), and this can be added by making a new DOCGEN_Foo
component in the allPlusDocgen
method, and then inheriting from that.
Todo
- Support
PropTypes.oneOfType
- Better support and testing of array props
- Colors
- More testing/demos
- Check all components are included - see https://github.com/mui-org/material-ui/blob/master/packages/material-ui/src/index.js
- ExpansionPanel onChange parameters
- Any other missing events from native elements? E.g. onClick on MenuItem is not documented.
- Aria properties
- id, ref, etc.
- Detect
classes
prop and accept an appropriate type? - Consider changing PascalCase prop names to camelCase to be more idiomatic. Consider typing these based on a map from prop names to the Component name of the sub element. This would still allow use of a
js.Object
containing props, in anadditionalProps
field. - Support component methods
- Support for withStyles?
- Look at missing events onBlur, onEmpty, onFilled, onFocus, onKeyDown, onKeyUp in Input, NativeSelect, Select