|
13 | 13 | // limitations under the License. |
14 | 14 |
|
15 | 15 | import { |
| 16 | + ancestorThat, |
16 | 17 | bindEventListener, |
17 | 18 | elementIsEditable, |
18 | 19 | findRef, |
19 | 20 | isOrContains, |
| 21 | + matchesSelector, |
20 | 22 | toHTMLElement, |
21 | 23 | } from './dom_utils'; |
22 | 24 |
|
@@ -162,3 +164,102 @@ describe('bindEventListener', () => { |
162 | 164 | }).not.toThrow(); |
163 | 165 | }); |
164 | 166 | }); |
| 167 | + |
| 168 | +describe('ancestorThat', () => { |
| 169 | + test('returns undefined for null element', () => { |
| 170 | + expect(ancestorThat(null, () => true)).toBeUndefined(); |
| 171 | + }); |
| 172 | + |
| 173 | + test('returns the element itself if it matches the predicate', () => { |
| 174 | + const el = document.createElement('div'); |
| 175 | + el.classList.add('target'); |
| 176 | + expect(ancestorThat(el, (e) => e.classList.contains('target'))).toBe(el); |
| 177 | + }); |
| 178 | + |
| 179 | + test('finds ancestor matching predicate', () => { |
| 180 | + const grandparent = document.createElement('div'); |
| 181 | + grandparent.classList.add('grandparent'); |
| 182 | + const parent = document.createElement('div'); |
| 183 | + parent.classList.add('parent'); |
| 184 | + const child = document.createElement('div'); |
| 185 | + child.classList.add('child'); |
| 186 | + |
| 187 | + grandparent.appendChild(parent); |
| 188 | + parent.appendChild(child); |
| 189 | + |
| 190 | + expect(ancestorThat(child, (e) => e.classList.contains('parent'))).toBe( |
| 191 | + parent, |
| 192 | + ); |
| 193 | + expect( |
| 194 | + ancestorThat(child, (e) => e.classList.contains('grandparent')), |
| 195 | + ).toBe(grandparent); |
| 196 | + }); |
| 197 | + |
| 198 | + test('returns closest matching ancestor', () => { |
| 199 | + const outer = document.createElement('div'); |
| 200 | + outer.classList.add('match'); |
| 201 | + const inner = document.createElement('div'); |
| 202 | + inner.classList.add('match'); |
| 203 | + const child = document.createElement('div'); |
| 204 | + |
| 205 | + outer.appendChild(inner); |
| 206 | + inner.appendChild(child); |
| 207 | + |
| 208 | + expect(ancestorThat(child, (e) => e.classList.contains('match'))).toBe( |
| 209 | + inner, |
| 210 | + ); |
| 211 | + }); |
| 212 | + |
| 213 | + test('returns undefined when no ancestor matches', () => { |
| 214 | + const parent = document.createElement('div'); |
| 215 | + const child = document.createElement('div'); |
| 216 | + parent.appendChild(child); |
| 217 | + |
| 218 | + expect( |
| 219 | + ancestorThat(child, (e) => e.classList.contains('nonexistent')), |
| 220 | + ).toBeUndefined(); |
| 221 | + }); |
| 222 | + |
| 223 | + test('does not match non-HTMLElement ancestors', () => { |
| 224 | + const svgElement = document.createElementNS( |
| 225 | + 'http://www.w3.org/2000/svg', |
| 226 | + 'svg', |
| 227 | + ); |
| 228 | + expect(ancestorThat(svgElement, () => true)).toBeUndefined(); |
| 229 | + }); |
| 230 | +}); |
| 231 | + |
| 232 | +describe('matchesSelector', () => { |
| 233 | + test('matchesSelector for a single class', () => { |
| 234 | + const el = document.createElement('div'); |
| 235 | + el.classList.add('foo'); |
| 236 | + |
| 237 | + const hasFoo = matchesSelector('.foo'); |
| 238 | + const hasBar = matchesSelector('.bar'); |
| 239 | + |
| 240 | + expect(hasFoo(el)).toBe(true); |
| 241 | + expect(hasBar(el)).toBe(false); |
| 242 | + }); |
| 243 | + |
| 244 | + test('checks for multiple classes', () => { |
| 245 | + const el = document.createElement('div'); |
| 246 | + el.classList.add('foo', 'bar'); |
| 247 | + |
| 248 | + const hasFooAndBar = matchesSelector('.foo.bar'); |
| 249 | + const hasFooAndBaz = matchesSelector('.foo.baz'); |
| 250 | + |
| 251 | + expect(hasFooAndBar(el)).toBe(true); |
| 252 | + expect(hasFooAndBaz(el)).toBe(false); |
| 253 | + }); |
| 254 | + |
| 255 | + test('works with ancestorThat', () => { |
| 256 | + const parent = document.createElement('div'); |
| 257 | + parent.classList.add('container', 'active'); |
| 258 | + const child = document.createElement('div'); |
| 259 | + parent.appendChild(child); |
| 260 | + |
| 261 | + expect(ancestorThat(child, matchesSelector('.container'))).toBe(parent); |
| 262 | + expect(ancestorThat(child, matchesSelector('.container.active'))).toBe(parent); |
| 263 | + expect(ancestorThat(child, matchesSelector('.container.inactive'))).toBeUndefined(); |
| 264 | + }); |
| 265 | +}); |
0 commit comments