Skip to content
This repository was archived by the owner on Jan 13, 2025. It is now read-only.

Commit 7cc3dc8

Browse files
authored
fix(ripple): Ripple should not activate on disabled label click (#532)
1 parent 248734e commit 7cc3dc8

File tree

9 files changed

+38
-3
lines changed

9 files changed

+38
-3
lines changed

demos/checkbox.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ <h2>With Javascript</h2>
159159
</div>
160160
<button type="button" id="make-ind">Make indeterminate</button>
161161
</section>
162+
162163
<section class="example mdc-theme--dark">
163164
<h2>Dark Theme</h2>
164165
<div class="mdc-form-field">
@@ -192,6 +193,9 @@ <h2>Dark Theme</h2>
192193
var checkbox = document.getElementById('mdc-js-checkbox');
193194
var checkboxInstance = new MDCCheckbox(checkbox);
194195

196+
var disabledCheckbox = document.getElementById('mdc-disabled-js-checkbox');
197+
var disabledCheckboxInstance = new MDCCheckbox(disabledCheckbox);
198+
195199
var formField = checkbox.parentElement;
196200
var formFieldInstance = new MDCFormField(formField);
197201
formFieldInstance.input = checkboxInstance;

packages/mdc-checkbox/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ The adapter for checkboxes must provide the following functions, with correct si
211211
| `forceLayout() => void` | Force-trigger a layout on the root element. This is needed to restart animations correctly. If you find that you do not need to do this, you can simply make it a no-op. |
212212
| `isAttachedToDOM() => boolean` | Returns true if the component is currently attached to the DOM, false otherwise.` |
213213

214+
214215
#### MDCCheckboxFoundation API
215216

216217
##### MDCCheckboxFoundation.isChecked() => boolean

packages/mdc-radio/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ Since MDC Radio is primarily driven by its native control, the adapter API is ex
162162
| `addClass(className: string) => void` | Adds a class to the root element. |
163163
| `removeClass(className: string) => void` | Removes a class from the root element. |
164164

165+
165166
#### The full foundation API
166167

167168
##### MDCRadioFoundation.isChecked() => boolean

packages/mdc-ripple/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ ripple to. The adapter API is as follows:
261261
| `browserSupportsCssVars() => boolean` | Whether or not the given browser supports CSS Variables. When implementing this, please take the [Safari considerations](#caveat-safari) into account. We provide a `supportsCssVariables` function within the `util.js` which we recommend using, as it handles this for you. |
262262
| `isUnbounded() => boolean` | Whether or not the ripple should be considered unbounded. |
263263
| `isSurfaceActive() => boolean` | Whether or not the surface the ripple is acting upon is [active](https://www.w3.org/TR/css3-selectors/#useraction-pseudos). We use this to detect whether or not a keyboard event has activated the surface the ripple is on. This does not need to make use of `:active` (which is what we do); feel free to supply your own heuristics for it. |
264+
| `isSurfaceDisabled() => boolean` | Whether or not the ripple is attached to a disabled component. If true, the ripple will not activate. |
264265
| `addClass(className: string) => void` | Adds a class to the ripple surface |
265266
| `removeClass(className: string) => void` | Removes a class from the ripple surface |
266267
| `registerInteractionHandler(evtType: string, handler: EventListener) => void` | Registers an event handler that's invoked when the ripple is interacted with using type `evtType`. Essentially equivalent to `HTMLElement.prototype.addEventListener`. |

packages/mdc-ripple/foundation.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ export default class MDCRippleFoundation extends MDCFoundation {
4545
browserSupportsCssVars: () => /* boolean - cached */ {},
4646
isUnbounded: () => /* boolean */ {},
4747
isSurfaceActive: () => /* boolean */ {},
48+
isSurfaceDisabled: () => /* boolean */ {},
4849
addClass: (/* className: string */) => {},
4950
removeClass: (/* className: string */) => {},
5051
registerInteractionHandler: (/* evtType: string, handler: EventListener */) => {},
@@ -67,6 +68,7 @@ export default class MDCRippleFoundation extends MDCFoundation {
6768

6869
constructor(adapter) {
6970
super(Object.assign(MDCRippleFoundation.defaultAdapter, adapter));
71+
7072
this.layoutFrame_ = 0;
7173
this.frame_ = {width: 0, height: 0};
7274
this.activationState_ = this.defaultActivationState_();
@@ -142,6 +144,10 @@ export default class MDCRippleFoundation extends MDCFoundation {
142144
}
143145

144146
activate_(e) {
147+
if (this.adapter_.isSurfaceDisabled()) {
148+
return;
149+
}
150+
145151
const {activationState_: activationState} = this;
146152
if (activationState.isActivated) {
147153
return;

packages/mdc-ripple/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export class MDCRipple extends MDCComponent {
3737
browserSupportsCssVars: () => supportsCssVariables(window),
3838
isUnbounded: () => instance.unbounded,
3939
isSurfaceActive: () => instance.root_[MATCHES](':active'),
40+
isSurfaceDisabled: () => instance.disabled,
4041
addClass: (className) => instance.root_.classList.add(className),
4142
removeClass: (className) => instance.root_.classList.remove(className),
4243
registerInteractionHandler: (evtType, handler) => instance.root_.addEventListener(evtType, handler),

test/unit/mdc-ripple/foundation-activation.test.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,20 @@ import {cssClasses, strings, numbers} from '../../../packages/mdc-ripple/constan
2121

2222
suite('MDCRippleFoundation - Activation Logic');
2323

24+
testFoundation('does nothing if component if isSurfaceDisabled is true',
25+
({foundation, adapter, mockRaf}) => {
26+
const handlers = captureHandlers(adapter);
27+
foundation.init();
28+
mockRaf.flush();
29+
30+
td.when(adapter.isSurfaceDisabled()).thenReturn(true);
31+
32+
handlers.mousedown();
33+
34+
td.verify(adapter.addClass(cssClasses.BG_ACTIVE_FILL), {times: 0});
35+
td.verify(adapter.addClass(cssClasses.FG_ACTIVATION), {times: 0});
36+
});
37+
2438
testFoundation('adds activation classes on mousedown', ({foundation, adapter, mockRaf}) => {
2539
const handlers = captureHandlers(adapter);
2640
foundation.init();

test/unit/mdc-ripple/foundation.test.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,10 @@ test('numbers returns constants.numbers', () => {
3939

4040
test('defaultAdapter returns a complete adapter implementation', () => {
4141
verifyDefaultAdapter(MDCRippleFoundation, [
42-
'browserSupportsCssVars', 'isUnbounded', 'isSurfaceActive', 'addClass', 'removeClass',
43-
'registerInteractionHandler', 'deregisterInteractionHandler', 'registerResizeHandler',
44-
'deregisterResizeHandler', 'updateCssVariable', 'computeBoundingRect', 'getWindowPageOffset',
42+
'browserSupportsCssVars', 'isUnbounded', 'isSurfaceActive', 'isSurfaceDisabled',
43+
'addClass', 'removeClass', 'registerInteractionHandler', 'deregisterInteractionHandler',
44+
'registerResizeHandler', 'deregisterResizeHandler', 'updateCssVariable',
45+
'computeBoundingRect', 'getWindowPageOffset',
4546
]);
4647
});
4748

test/unit/mdc-ripple/mdc-ripple.test.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,12 @@ test('adapter#isSurfaceActive calls the correct :matches API method on the root
111111
assert.isOk(component.getDefaultFoundation().adapter_.isSurfaceActive());
112112
});
113113

114+
test('adapter#isSurfaceDisabled delegates to component\'s disabled getter', () => {
115+
const {component} = setupTest();
116+
component.disabled = true;
117+
assert.isTrue(component.getDefaultFoundation().adapter_.isSurfaceDisabled());
118+
});
119+
114120
test('adapter#addClass adds a class to the root', () => {
115121
const {root, component} = setupTest();
116122
component.getDefaultFoundation().adapter_.addClass('foo');

0 commit comments

Comments
 (0)