From f7ffd40e2de47f154e51e68b147120c22d29bb53 Mon Sep 17 00:00:00 2001 From: Jack Steinberg Date: Thu, 11 Jul 2019 10:18:30 -0700 Subject: [PATCH] Create toast action setter Additionally, this CL adds support for passing Elements as action in showToast, using the action setter. This change is the latest in a series of changes implementing new behavior added to the toast explainer in PR #43 https://github.com/jackbsteinberg/std-toast/pull/43. Bug: 972945 Change-Id: I46004883cb3b794981029aa02369918dd0077391 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1686786 Commit-Queue: Jack Steinberg Reviewed-by: Fergal Daly Cr-Commit-Position: refs/heads/master@{#676482} --- std-toast/actions.html | 85 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/std-toast/actions.html b/std-toast/actions.html index 018ce21128bddd..d07e5fd3d62694 100644 --- a/std-toast/actions.html +++ b/std-toast/actions.html @@ -102,4 +102,89 @@ assert_equals(actionButton, null); }, 'passing non-string (undefined) as action option does not create an action button'); + +testToastElement((toast) => { + const actionButton = document.createElement('button'); + actionButton.textContent = 'action'; + toast.action = actionButton; + + assertActionButtonOnToast(actionButton, toast); +}, 'setting the action on an actionless toast inserts the element into the slot'); + +testActionToast((toast, action) => { + const actionButton = document.createElement('button'); + actionButton.textContent = 'replacement'; + toast.action = actionButton; + + assert_false(document.contains(action)); + assertActionButtonOnToast(actionButton, toast); +}, 'resetting the action on an action toast changes the action element'); + +testToastElement((toast) => { + const text = document.createTextNode('some text'); + assert_throws(new TypeError(), () => { + toast.action = text; + }); +}, 'setting the action to an invalid type (Text node) throws an error'); + +testToastElement((toast) => { + const text = 'some text'; + assert_throws(new TypeError(), () => { + toast.action = text; + }); +}, 'setting the action to an invalid type (string) throws an error'); + +test(() => { + const actionButton = document.createElement('button'); + actionButton.textContent = 'action'; + const toast = showToast('Message', {action: actionButton}); + + assertActionButtonOnToast(actionButton, toast); +}, 'showToast can take an Element as the action parameter'); + +testActionToast((toast, action) => { + toast.action = null; + + assert_not_equals(toast.action, action); + assert_equals(toast.querySelector('button'), null); +}, 'setting toast.action to null removes the action from the toast'); + +testActionToast((toast, action) => { + const wrongAction = document.createElement('button'); + wrongAction.textContent = 'wrong'; + wrongAction.setAttribute('slot', 'action'); + toast.appendChild(wrongAction); + + const correctAction = document.createElement('button'); + correctAction.textContent = 'correct'; + toast.action = correctAction; + + assertActionButtonOnToast(correctAction, toast); +}, 'resetting toast.action on a toast with multiple actions slotted sets properly'); + +test(() => { + try { + Object.defineProperty(Element, Symbol.hasInstance, { + value: () => true, + configurable: true + }); + + const fakeElement = {}; + const toast = showToast('Message'); + assert_throws(new TypeError(), () => toast.action = fakeElement); + } finally { + delete Element[Symbol.hasInstance]; + } +}, 'spoofing element instance will not register as element to action setter'); + +test(() => { + const iframe = document.createElement('iframe'); + document.body.append(iframe); + iframe.contentDocument.body.innerHTML = '
'; + const elementFromAnotherFrame = iframe.contentDocument.querySelector('div'); + + // Should not throw: + const toast = showToast('Message'); + toast.action = elementFromAnotherFrame; +}, 'element from iframe instance will pass correctly to action without throwing an error');