-
Notifications
You must be signed in to change notification settings - Fork 227
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
feat(switch): Add component #208
Conversation
packages/ripple/README.md
Outdated
|
||
You may want to apply the visual treatment (CSS classes and styles) for a ripple surface on one element, but have its activation rely on a different element. For example, putting a ripple on a `<div>` which will be activated by focusing on a child `<input>` element. We call the visual element the "ripple surface" and the activating element the "ripple activator". | ||
|
||
The `initRipple` callback prop will take in an extra `activator` argument for the case where the ripple activator differs from the ripple surface. If the `activator` argument is not provided, the ripple surface will also serve as the ripple activator. |
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.
prop will take in --> prop can take in?
packages/ripple/README.md
Outdated
this.props.initRipple(el /* surface */, this.rippleActivatorElement /* activator */); | ||
} | ||
|
||
setRippleActivator = (el) => { |
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 should be telling devs to use React.createRef for these situations.
packages/switch/NativeControl.js
Outdated
|
||
export default class NativeControl extends React.Component { | ||
componentDidMount() { | ||
this.props.handleChange(this.props.checked); |
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 don't think you need to call these here since they are initialized in state in index.js
packages/switch/NativeControl.js
Outdated
handleChange = (e) => { | ||
const {handleChange, onChange} = this.props; | ||
const {checked} = e.target; | ||
handleChange(checked); |
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.
Can an onChange
be passed in that calls handleChange
in the parent function? Then you don't need 2 change functions being passed to this component.
packages/switch/NativeControl.js
Outdated
id: PropTypes.string, | ||
onChange: PropTypes.func, | ||
handleDisabled: PropTypes.func, | ||
handleChange: PropTypes.func, |
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.
make sure to remove these if they're unnecessary
packages/switch/ThumbUnderlay.js
Outdated
disabled={disabled} | ||
handleChange={handleChange} | ||
handleDisabled={handleDisabled} | ||
setrippleActivatorEl={(el) => { |
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.
use React.createRef() here. Also setRippleActivator
**
packages/switch/ThumbUnderlay.js
Outdated
} | ||
|
||
get classes() { | ||
return classnames('mdc-switch__thumb-underlay', this.props.className); |
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.
Is this supposed to have the same this.props.className from the parent? It looks like classes will be duplicated here from index.js. I don't think that is the intention.
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 don't think so? The classes from index.js aren't passed down to ThumbUnderlay
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.
Gotcha - you're just not passing any classes from index.js to the ThumbUnderlay component so it seemed like you didn't need this. But I guess it doesn't hurt to have this.
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.
Oh I see what you're saying. ThumbUnderlay
is actually receiving props.className
from withRipple
, so it's necessary. On the other hand NativeControl
isn't getting classes from anywhere, so I removed it there!
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.
ah! i didn't even notice :)
packages/switch/index.js
Outdated
<ThumbUnderlay | ||
checked={this.state.checked} | ||
disabled={disabled} | ||
id={id} |
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.
is the ID for a aria-label relationship?
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.
Yup! I renamed it nativeControlId
to make it clearer
test/screenshot/full-suite.js
Outdated
require('./select/screenshot-suite').default, | ||
require('./switch/screenshot-suite').default, |
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.
double
Codecov Report
@@ Coverage Diff @@
## master #208 +/- ##
==========================================
- Coverage 98.86% 98.73% -0.13%
==========================================
Files 23 26 +3
Lines 969 1028 +59
Branches 98 102 +4
==========================================
+ Hits 958 1015 +57
- Misses 11 13 +2
Continue to review full report at Codecov.
|
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.
lint is failing as well. Also i left a comment about this but there is no way for the user to get a hold of the checked value.
packages/switch/ThumbUnderlay.js
Outdated
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import classnames from 'classnames'; | ||
import withRipple from '../ripple'; |
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.
for now we should be using @material/ripple until we move to a monopackage.
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.
But ripple is modified in this PR so it won't work if it uses @material/ripple :(
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.
even if you do lerna add @material/ripple --scope=@material/react-switch
?
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.
it worked!
packages/switch/ThumbUnderlay.js
Outdated
const {handleChange, onChange} = this.props; | ||
const {checked} = e.target; | ||
handleChange(checked); | ||
onChange(e); |
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 don't think this should be here. You're not passing onChange in index.js to .
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.
Ok makes sense since the user wouldn't have access to the ThumbUnderlay anyways. Removed.
packages/switch/index.js
Outdated
export default class Switch extends Component { | ||
foundation_ = null; | ||
state = { | ||
checked: this.props.checked, |
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.
oh cool - i didn't know this worked.
packages/switch/README.md
Outdated
render() { | ||
return ( | ||
<Switch nativeControlId='my-switch' /> | ||
<label for='my-switch'>My Switch</label> |
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.
for -> htmlFor
import "@material/react-switch/dist/switch.css"; | ||
``` | ||
|
||
### Javascript Instantiation |
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.
how does one get a hold of the value? if its checked/not checked
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.
Added a note to the README, basically same way as in text field
const MATCHES = util.getMatchesProperty(HTMLElement.prototype); | ||
|
||
return { | ||
browserSupportsCssVars: () => util.supportsCssVariables(window), | ||
isUnbounded: () => this.props.unbounded, | ||
isSurfaceActive: () => instance[MATCHES](':active'), | ||
isSurfaceActive: () => activator ? activator[MATCHES](':active') : surface[MATCHES](':active'), |
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.
this is so simple and lovely. I wonder if we could simplify the Vanilla version of Checkbox and Switch in MDCW to look more like this.
https://github.com/material-components/material-components-web/blob/master/packages/mdc-switch/index.js#L79
https://github.com/material-components/material-components-web/blob/master/packages/mdc-checkbox/index.js#L63
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 probably modify createAdapter
to take in an extra param as well
packages/ripple/index.js
Outdated
@@ -148,7 +145,13 @@ const withRipple = (WrappedComponent) => { | |||
this.setState({style: updatedStyle}); | |||
} | |||
|
|||
getMergedStyles = () => { | |||
get classes() { | |||
const {className: wrappedCompClasses} = this.props; |
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.
wrappedComponentClasses. Don't shorten it
packages/switch/NativeControl.js
Outdated
disabled: PropTypes.bool, | ||
id: PropTypes.string, | ||
onChange: PropTypes.func, | ||
setRippleActivator: PropTypes.oneOfType([ |
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.
can this just be rippleActivator
, which I assume is an Element, since it gets passed to a ref
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.
It's a ref
which gets this PropType definition according to StackOverflow. Changed the name to rippleActivatorRef
to make it clearer.
packages/switch/README.md
Outdated
|
||
Prop Name | Type | Description | ||
--- | --- | --- | ||
className | String | Classes to be applied to the chip element |
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.
hmmm, not a chip
packages/switch/index.js
Outdated
classList.delete(className); | ||
this.setState({classList}); | ||
}, | ||
isNativeControlChecked: () => this.state.checked, |
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.
is it weird that we implement isNativeControlChecked
by look at the root component's state? I guess we are basically guaranteeing that if the native control's state changes, then ThumbUnderlay will respond in an onChange
callback. And then if ThumbUnderlay's state changes we guarantee that Switch's state will change also with and handleChange
callback.
But for some reason that feels a bit fragile. Did we do the same design for the text field?
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.
Yep, but text field only has one layer of nesting (is nesting the right word?) TextField
-> Input
which makes it seem less fragile. And similarly Select
->NativeControl
. Switch is the only component for now with 3 layers because it needs one for the native input, one for the ripple surface, and one for the top-level component.
packages/switch/ThumbUnderlay.js
Outdated
className: PropTypes.string, | ||
disabled: PropTypes.bool, | ||
nativeControlId: PropTypes.string, | ||
handleChange: PropTypes.func, |
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.
why not onChange
? Why handleChange
?
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 think on*
conventionally expects an event parameter. Confirm, @moog16?
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've been using on*
as an event handler. I think we invented handle*
so it wouldn't collide with an end developer. But in this case the end dev won't ever pass an onChange method so we can change it to onChange
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.
👍 Updated!
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 think add a test for the Thumbunderlay's onChange={(e) => onChange(e.target.checked)}
. But the rest looks good to me! Nice work 🗡
I pushed some changes to this PR. But We it now relies on the Or we could do some work around for now? I'd rather wait than think about hacks, but maybe someone has a clever idea |
Ya I agree - no sense in making a work around and rush getting this PR in. We can wait until MDCW releases 0.39.0. Then we can update MDCR repo, and get this PR working. Then release 0.5.0 |
packages/switch/NativeControl.js
Outdated
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
|
||
export default class NativeControl extends React.Component { |
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.
change this to stateless component:
const NativeControl = (props) => {
return (...)
}
export default NativeControl;
packages/switch/NativeControl.js
Outdated
<input | ||
type='checkbox' | ||
role='switch' | ||
className='mdc-switch__native-control' |
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.
Do you think people will use this component on its own? If so we should merge this.props.className and 'mdc-switch__native-control'
packages/switch/index.js
Outdated
export default class Switch extends Component { | ||
rippleActivator = React.createRef(); | ||
foundation_ = null; | ||
state = { |
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 think we should use constructor to set initial state:
constructor(props) {
super(props);
rippleActivator = React.createRef();
foundation_ = null;
this.state = {
checked: props.checked,
classList: new Set(),
disabled: props.disabled,
nativeControlChecked: props.checked,
nativeControlDisabled: props.disabled,
};
}
packages/switch/package.json
Outdated
"name": "@material/react-switch", | ||
"version": "0.0.0", | ||
"description": "Material Components React Switch", | ||
"license": "Apache-2.0", |
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.
MIT!
packages/switch/package.json
Outdated
}, | ||
"dependencies": { | ||
"@material/react-ripple": "^0.4.0", | ||
"@material/ripple": "^0.38.0", |
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.
ripple needs to be 39
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.
actually remove this and only rely on react-ripply
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.
This doesn't work:
class TestSwitch extends React.Component {
state = {checked: true};
render() {
return (
<div>
<Switch checked={this.state.checked} />
<button onClick={() => this.setState({checked: !this.state.checked})}>
update
</button>
</div>
)
}
}
Fixes #152.
Ripple is modified because the element with the ripple surface (thumb underlay) is different from the ripple activating element (native input).