-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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): Update switch to use JS and have ripple #2915
Conversation
We found a Contributor License Agreement for you (the sender of this pull request), but were unable to find agreements for all the commit author(s) or Co-authors. If you authored these, maybe you used a different email address in the git commits than was used to sign the CLA (login here to double check)? If these were authored by someone else, then they will need to sign a CLA as well, and confirm that they're okay with these being contributed to Google. |
demos/theme/index.html
Outdated
@@ -697,7 +697,7 @@ <h3 class="mdc-typography--headline5 demo-component-section__heading"> | |||
<div class="mdc-switch"> | |||
<input type="checkbox" id="basic-switch" class="mdc-switch__native-control" role="switch" checked> | |||
<div class="mdc-switch__background"> | |||
<div class="mdc-switch__knob"></div> | |||
<div class="mdc-switch__thumb"></div> |
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.
HTML structure needs to be updated here too.
Same below.
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.
Done
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.
Shouldn't the input tag be inside __thumb as per new HTML structure?
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.
Thanks, done!
@@ -62,6 +63,7 @@ autoInit.register('MDCTextField', textField.MDCTextField); | |||
autoInit.register('MDCMenu', menu.MDCMenu); | |||
autoInit.register('MDCSelect', select.MDCSelect); | |||
autoInit.register('MDCSlider', slider.MDCSlider); | |||
autoInit.register('MDCSwitch', switchComponent.MDCSwitch); |
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.
switch.MDCSwitch
?
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.
Unfortunately not possible because switch is a reserved word.
|
||
# Switches | ||
|
||
<!--<div class="article__asset"> |
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.
Please remove commented code.
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 used for the site generator: https://github.com/material-components/material-components-site-generator
packages/mdc-switch/README.md
Outdated
</a> | ||
</div>--> | ||
|
||
Switches toggle the state of a single settings option on or off, and are mobile preferred. |
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.
"Switches toggle the state of a single setting on or off. They are the preferred way to adjust settings on mobile."
from Spec.
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.
Done.
<div class="mdc-switch__track"></div> | ||
<div class="mdc-switch__thumb-underlay"> | ||
<div class="mdc-switch__thumb"> | ||
<input type="checkbox" id="basic-switch" class="mdc-switch__native-control" role="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.
We might not need to add id
attribute for usage.
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.
The ids are used to demonstrate using switches with labels with for="id" attributes, so I think we'll want to leave them.
} | ||
|
||
/** @return {!MDCRipple} */ | ||
get 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.
should this property be public?
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.
There seems to be a public getter exposed for all of the components with ripple, so I went with that as well here.
packages/mdc-switch/mdc-switch.scss
Outdated
mdc-switch-transition(background-color), | ||
mdc-switch-transition(border-color); | ||
border: 1px solid; | ||
border-radius: 7px; |
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.
$mdc-switch-track-height / 2
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.
Done
demos/switch.scss
Outdated
@include mdc-switch-knob-color($color); | ||
@include mdc-switch-focus-indicator-color($color); | ||
|
||
@include mdc-switch-toggled-on-track-color($color); |
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 it makes sense to create a new mixin which packages these three mixins?
mdc-switch-toggle-color($color);
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 do that to make it simple to customize more easily and also do the same for toggled off. Would you be ok with calling the default something like mdc-switch-toggled-on-color? Without the 'on' it might not be clear what the mixin is doing (as it could be referring to either 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.
Yah, since the mixin applies color to only ON state it makes sense to name it mdc-switch-toggled-on-color
.
Update the docs too.
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.
Done
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 updated code based on comments and also now made it so that the tap target (input element) covers the whole switch and stays stationary. This means that if you click on the ripple to check/uncheck the switch, you can click in the same area again to reverse that. Previously, once the switch was toggled, that area was no longer clickable.
demos/switch.scss
Outdated
@include mdc-switch-knob-color($color); | ||
@include mdc-switch-focus-indicator-color($color); | ||
|
||
@include mdc-switch-toggled-on-track-color($color); |
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 do that to make it simple to customize more easily and also do the same for toggled off. Would you be ok with calling the default something like mdc-switch-toggled-on-color? Without the 'on' it might not be clear what the mixin is doing (as it could be referring to either state).
|
||
# Switches | ||
|
||
<!--<div class="article__asset"> |
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 used for the site generator: https://github.com/material-components/material-components-site-generator
<div class="mdc-switch__track"></div> | ||
<div class="mdc-switch__thumb-underlay"> | ||
<div class="mdc-switch__thumb"> | ||
<input type="checkbox" id="basic-switch" class="mdc-switch__native-control" role="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.
The ids are used to demonstrate using switches with labels with for="id" attributes, so I think we'll want to leave them.
demos/theme/index.html
Outdated
@@ -697,7 +697,7 @@ <h3 class="mdc-typography--headline5 demo-component-section__heading"> | |||
<div class="mdc-switch"> | |||
<input type="checkbox" id="basic-switch" class="mdc-switch__native-control" role="switch" checked> | |||
<div class="mdc-switch__background"> | |||
<div class="mdc-switch__knob"></div> | |||
<div class="mdc-switch__thumb"></div> |
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.
Done
packages/mdc-switch/README.md
Outdated
</a> | ||
</div>--> | ||
|
||
Switches toggle the state of a single settings option on or off, and are mobile preferred. |
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.
Done.
packages/mdc-switch/mdc-switch.scss
Outdated
mdc-switch-transition(background-color), | ||
mdc-switch-transition(border-color); | ||
border: 1px solid; | ||
border-radius: 7px; |
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.
Done
demos/switch.scss
Outdated
@include mdc-switch-knob-color($color); | ||
@include mdc-switch-focus-indicator-color($color); | ||
|
||
@include mdc-switch-toggled-on-track-color($color); |
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.
Yah, since the mixin applies color to only ON state it makes sense to name it mdc-switch-toggled-on-color
.
Update the docs too.
demos/theme/index.html
Outdated
@@ -697,7 +697,7 @@ <h3 class="mdc-typography--headline5 demo-component-section__heading"> | |||
<div class="mdc-switch"> | |||
<input type="checkbox" id="basic-switch" class="mdc-switch__native-control" role="switch" checked> | |||
<div class="mdc-switch__background"> | |||
<div class="mdc-switch__knob"></div> | |||
<div class="mdc-switch__thumb"></div> |
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.
Shouldn't the input tag be inside __thumb as per new HTML structure?
packages/mdc-switch/adapter.js
Outdated
@@ -0,0 +1,53 @@ | |||
/** | |||
* @license | |||
* Copyright 2016 Google Inc. All Rights Reserved. |
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.
nit: 2018
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.
Done
packages/mdc-switch/constants.js
Outdated
/** @enum {string} */ | ||
const strings = { | ||
NATIVE_CONTROL_SELECTOR: `.${ROOT}__native-control`, | ||
RIPLE_SURFACE_SELECTOR: `.${ROOT}__thumb-underlay`, |
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.
nit: RIPPLE_SURFACE_SELECTOR*
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.
Thanks, done!
@@ -0,0 +1,118 @@ | |||
/** |
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.
Update README for MDCSwitchFoundation & MDCSwitchAdapter.
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 catch! Updated.
…king the native control in the foundation
@import "./variables"; | ||
|
||
@mixin mdc-switch-toggled-on-color($color) { |
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.
These are all super clean. Nice!!!
packages/mdc-switch/_variables.scss
Outdated
|
||
$mdc-switch-baseline-theme-color: secondary; | ||
|
||
$mdc-switch-thumb-vertical-offset_: -3px; |
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.
Could you add a comment explaining why this is a negative 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.
Will do, I think this was more of a remnant from before and I think I can just add this into the top value for the underlay instead.
packages/mdc-switch/adapter.js
Outdated
*/ | ||
|
||
/* eslint-disable no-unused-vars */ | ||
import {MDCSelectionControlState} from '@material/selection-control/index'; |
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.
Where in this file is MDCSelectionControlState used?
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 catch, no longer being used here or in foundation.js.
packages/mdc-switch/constants.js
Outdated
|
||
/** @enum {string} */ | ||
const strings = { | ||
NATIVE_CONTROL_SELECTOR: `.${ROOT}__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.
Since you don't use the ROOT constant in the cssClasses, I'd be tempted to remove it and just have these be normal strings instead of template strings. It's not exported either so it's not doing much.
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.
SGTM!
/** @return {!MDCSwitchAdapter} */ | ||
static get defaultAdapter() { | ||
return /** @type {!MDCSwitchAdapter} */ ({ | ||
addClass: (/* className: string */) => {}, |
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'm not sure if these annotations for faux-parameter types are necessary. Does Closure still pass if they're removed?
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 it would yes, but I was just basing this on looking at most of the other foundation get defaultAdapter methods which do this and assumed this was the preferred style. Do you know if it's cool to move away from this now or if there's a reason the other files are doing it?
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.
AFAIK, these are not closure annotations :)
We might be duplicating the defining the types in foundation & adapter file.
packages/mdc-switch/foundation.js
Outdated
/** @param {boolean} disabled */ | ||
setDisabled(disabled) { | ||
this.adapter_.setDisabled(disabled); | ||
disabled ? this.adapter_.addClass(cssClasses.DISABLED) : this.adapter_.removeClass(cssClasses.DISABLED); |
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'm not a huge fan of these ternaries. I'd suggest making it an if
statement with an early-exit return or a normal if
/else
. I totally get that's just a reworking of what this does. I just find it easier to read.
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'm fine either way, so will switch to if/else.
packages/mdc-switch/foundation.js
Outdated
* Updates the styling of the switch based on its checked state. | ||
* @private | ||
*/ | ||
updateCheckedStyling_() { |
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'd suggest giving this a isChecked
boolean argument and rewriting the check to use an if
with early return or if
/else
flow. Then, moving the this.isChecked()
call into handleChange()
since that seems to be the only place where you're uncertain of the checked
state. You can then update setChecked
to pass the checked
argument into updateCheckedStyling_
directly.
Let me know what you think!
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.
SGTM!
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.
LGTM.
Please also add a screenshot test for switch in separate PR.
demos/switch.html
Outdated
@@ -138,6 +147,12 @@ <h2>Disabled</h2> | |||
document.getElementById('toggle-rtl').addEventListener('change', function() { | |||
this.checked ? demoWrapper.setAttribute('dir', 'rtl') : demoWrapper.removeAttribute('dir'); | |||
}); | |||
demoReady(function() { | |||
const switches = document.querySelectorAll('.mdc-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.
const
=> var
We usually use ES5 here since the demo pages are not compiled.
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.
Sounds good
packages/mdc-switch/adapter.js
Outdated
* limitations under the License. | ||
*/ | ||
|
||
/* eslint-disable no-unused-vars */ |
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 comment line may no longer be required.
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.
Will remove
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 a version of this is still required, otherwise it complains about the unused params.
/** @return {!MDCSwitchAdapter} */ | ||
static get defaultAdapter() { | ||
return /** @type {!MDCSwitchAdapter} */ ({ | ||
addClass: (/* className: string */) => {}, |
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.
AFAIK, these are not closure annotations :)
We might be duplicating the defining the types in foundation & adapter file.
packages/mdc-switch/adapter.js
Outdated
isChecked() {} | ||
|
||
/** @param {boolean} disabled */ | ||
setDisabled(disabled) {} |
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.
Suggestion: should we name it setNativeDisabled()
?
it might get confused with the Foundation's setDisabled()
method. WDYT?
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.
That's a good point, perhaps (although a bit longer) it should be setNativeControlChecked/setNativeControlDisabled as to match other components with native control elements.
packages/mdc-switch/mdc-switch.scss
Outdated
display: flex; | ||
position: absolute; | ||
// Ensures the knob is centered on the track. | ||
top: -17px; |
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.
Mathematically this is -($mdc-switch-track-height / 2 + $mdc-switch-thumb-diameter / 2)
:)
The distance between the center of the track and the thumb relative to its original position.
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 can switch to that to be more clear!
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'm all about it. My only change is to remove the [de]registerChangeHandler
methods from the adapter. That will require registering and deregistering the listeners manually in the index.js/MDCComponent
file.
There's a good example in the single-selection list item PR showing what that looks like.
@@ -26,6 +26,7 @@ const checkbox = new mdc.checkbox.MDCCheckbox(document.querySelector('.mdc-check | |||
import { checkbox } from 'material-components-web'; | |||
const checkbox = new checkbox.MDCCheckbox(document.querySelector('.mdc-checkbox')); | |||
``` | |||
> NOTE: Since switch is a reserved word in JS, switch is instead named `switchControl`. |
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.
Nice explanation 👍
packages/mdc-switch/README.md
Outdated
| `removeClass(className: string) => void` | Removes a class from the root element. | | ||
| `registerChangeHandler(handler: EventListener) => void` | Registers an event handler to be called when a `change` event is triggered on the native control. | | ||
| `deregisterChangeHandler(handler: EventListener) => void` | Deregisters an event handler that was previously passed to `registerChangeHandler`. | | ||
| `setChecked(checked: boolean)` | Sets the checked state of the 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.
I think these need to be updated to use the new setNativeControlChecked
etc.
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.
Nice catch, thanks!
packages/mdc-switch/index.js
Outdated
removeClass: (className) => this.root_.classList.remove(className), | ||
registerChangeHandler: (handler) => this.nativeControl_.addEventListener('change', handler), | ||
deregisterChangeHandler: (handler) => this.nativeControl_.removeEventListener('change', handler), | ||
setNativeControlChecked: (checked) => this.nativeControl_.checked = 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 like these a lot 🔥
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.
One last suggestion, otherwise LGTM
packages/mdc-switch/README.md
Outdated
If you are using a JavaScript framework, such as React or Angular, you can create a switch for your framework. Depending on your needs, you can use the _Simple Approach: Wrapping MDC Web Vanilla Components_, or the _Advanced Approach: Using Foundations and Adapters_. Please follow the instructions [here](../../docs/integrating-into-frameworks.md). | ||
|
||
## Event Handlers | ||
If wrapping the switch component it is necessary to add an event handler for native control change events that calls the `handleChange` foundation method. For an example of this, see the [MDCSwitch](index.js) component `initialSyncWithDOM` method. |
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'd suggest making a table similar to the MDCSwitchAdapter
one. Something like this:
Event | Element Selector | Foundation Handler |
---|---|---|
"change" |
.mdc-switch__native-control |
handleChange() |
and maybe move it below the foundation section since it's it's related to the foundation.
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.
🔥🔥🔥
…rial-components-web into feat/new-switch
Codecov Report
@@ Coverage Diff @@
## feat/switch-update #2915 +/- ##
======================================================
+ Coverage 98.48% 98.49% +<.01%
======================================================
Files 98 101 +3
Lines 4232 4316 +84
Branches 538 540 +2
======================================================
+ Hits 4168 4251 +83
- Misses 64 65 +1
Continue to review full report at Codecov.
|
Addresses #2825