Skip to content
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

Merged
merged 20 commits into from
Jul 11, 2018

Conversation

rlfriedman
Copy link
Contributor

Addresses #2825

@googlebot
Copy link

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.
In order to pass this check, please resolve this problem and have the pull request author add another comment and the bot will run again. If the bot doesn't comment, it means it doesn't think anything has changed.

@@ -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>
Copy link
Contributor

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Contributor

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?

Copy link
Contributor Author

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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

switch.MDCSwitch?

Copy link
Contributor Author

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">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove commented code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

</a>
</div>-->

Switches toggle the state of a single settings option on or off, and are mobile preferred.
Copy link
Contributor

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.

Copy link
Contributor Author

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">
Copy link
Contributor

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.

Copy link
Contributor Author

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() {
Copy link
Contributor

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?

Copy link
Contributor Author

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.

mdc-switch-transition(background-color),
mdc-switch-transition(border-color);
border: 1px solid;
border-radius: 7px;
Copy link
Contributor

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@include mdc-switch-knob-color($color);
@include mdc-switch-focus-indicator-color($color);

@include mdc-switch-toggled-on-track-color($color);
Copy link
Contributor

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);

Copy link
Contributor Author

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).

Copy link
Contributor

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Copy link
Contributor Author

@rlfriedman rlfriedman left a 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.

@include mdc-switch-knob-color($color);
@include mdc-switch-focus-indicator-color($color);

@include mdc-switch-toggled-on-track-color($color);
Copy link
Contributor Author

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">
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<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">
Copy link
Contributor Author

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.

@@ -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>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

</a>
</div>-->

Switches toggle the state of a single settings option on or off, and are mobile preferred.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

mdc-switch-transition(background-color),
mdc-switch-transition(border-color);
border: 1px solid;
border-radius: 7px;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@include mdc-switch-knob-color($color);
@include mdc-switch-focus-indicator-color($color);

@include mdc-switch-toggled-on-track-color($color);
Copy link
Contributor

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.

@@ -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>
Copy link
Contributor

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?

@@ -0,0 +1,53 @@
/**
* @license
* Copyright 2016 Google Inc. All Rights Reserved.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: 2018

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

/** @enum {string} */
const strings = {
NATIVE_CONTROL_SELECTOR: `.${ROOT}__native-control`,
RIPLE_SURFACE_SELECTOR: `.${ROOT}__thumb-underlay`,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: RIPPLE_SURFACE_SELECTOR*

Copy link
Contributor Author

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 @@
/**
Copy link
Contributor

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! Updated.

@import "./variables";

@mixin mdc-switch-toggled-on-color($color) {
Copy link
Contributor

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!!!


$mdc-switch-baseline-theme-color: secondary;

$mdc-switch-thumb-vertical-offset_: -3px;
Copy link
Contributor

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?

Copy link
Contributor Author

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.

*/

/* eslint-disable no-unused-vars */
import {MDCSelectionControlState} from '@material/selection-control/index';
Copy link
Contributor

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?

Copy link
Contributor Author

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.


/** @enum {string} */
const strings = {
NATIVE_CONTROL_SELECTOR: `.${ROOT}__native-control`,
Copy link
Contributor

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.

Copy link
Contributor Author

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 */) => {},
Copy link
Contributor

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?

Copy link
Contributor Author

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?

Copy link
Contributor

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.

/** @param {boolean} disabled */
setDisabled(disabled) {
this.adapter_.setDisabled(disabled);
disabled ? this.adapter_.addClass(cssClasses.DISABLED) : this.adapter_.removeClass(cssClasses.DISABLED);
Copy link
Contributor

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.

Copy link
Contributor Author

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.

* Updates the styling of the switch based on its checked state.
* @private
*/
updateCheckedStyling_() {
Copy link
Contributor

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!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SGTM!

Copy link
Contributor

@abhiomkar abhiomkar left a 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.

@@ -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');
Copy link
Contributor

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good

* limitations under the License.
*/

/* eslint-disable no-unused-vars */
Copy link
Contributor

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will remove

Copy link
Contributor Author

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 */) => {},
Copy link
Contributor

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.

isChecked() {}

/** @param {boolean} disabled */
setDisabled(disabled) {}
Copy link
Contributor

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?

Copy link
Contributor Author

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.

display: flex;
position: absolute;
// Ensures the knob is centered on the track.
top: -17px;
Copy link
Contributor

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.

Copy link
Contributor Author

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!

@patrickrodee patrickrodee self-assigned this Jun 27, 2018
Copy link
Contributor

@patrickrodee patrickrodee left a 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`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice explanation 👍

| `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. |
Copy link
Contributor

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch, thanks!

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,
Copy link
Contributor

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 🔥

Copy link
Contributor

@patrickrodee patrickrodee left a 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

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.
Copy link
Contributor

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.

Copy link
Contributor

@patrickrodee patrickrodee left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔥🔥🔥

@codecov-io
Copy link

Codecov Report

Merging #2915 into feat/switch-update will increase coverage by <.01%.
The diff coverage is 98.8%.

Impacted file tree graph

@@                  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
Impacted Files Coverage Δ
packages/mdc-switch/constants.js 100% <100%> (ø)
packages/mdc-switch/foundation.js 100% <100%> (ø)
packages/mdc-switch/index.js 98% <98%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update e3e400d...cbae6f6. Read the comment docs.

@rlfriedman rlfriedman merged commit bedd674 into feat/switch-update Jul 11, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants