@@ -14,6 +14,69 @@ export type Diff<T> =
14
14
| { oldValue : null ; newValue : T }
15
15
| { oldValue : T ; newValue : T } ;
16
16
17
+ /**
18
+ * Type to filter out a difference based on `tagName`.`attributeName`
19
+ *
20
+ * The matcher can be a boolean or a `consumer` that returns a boolean
21
+ */
22
+ export interface DiffFilter < T > {
23
+ [ selector : string ] : DiffFilterSelector < T > ;
24
+ } ;
25
+
26
+ interface DiffFilterSelector < T > {
27
+ full ?: DiffFilterConsumer < T > ;
28
+ attributes ?: {
29
+ [ name : string ] : DiffFilterConsumer < T > ;
30
+ } ;
31
+ }
32
+
33
+ /**
34
+ * Consumer to match if a diff should be filtered out.
35
+ */
36
+ type DiffFilterConsumer < T > = boolean | ( ( value : T | null ) => boolean ) ;
37
+
38
+ function getDiffFilterSelector (
39
+ elementToBeCompared : Element ,
40
+ rootElementToBeCompared : Element ,
41
+ filters : DiffFilter < Element >
42
+ ) : DiffFilterSelector < Element > | undefined {
43
+ const querySelector : string | undefined =
44
+ rootElementToBeCompared === elementToBeCompared
45
+ ? ':scope'
46
+ : Object . keys ( filters ) . find ( selector =>
47
+ Array . from (
48
+ rootElementToBeCompared . querySelectorAll ( selector )
49
+ ) . includes ( elementToBeCompared )
50
+ ) ;
51
+
52
+ return querySelector ? filters [ querySelector ! ] : undefined ;
53
+ }
54
+
55
+ function shouldFilterElement (
56
+ element : Element ,
57
+ filter : DiffFilterSelector < Element > | undefined
58
+ ) : boolean {
59
+ if ( ! filter || ! filter . full ) {
60
+ return false ;
61
+ }
62
+ const consumer : DiffFilterConsumer < Element > = filter ! . full ! ;
63
+
64
+ return typeof consumer === 'boolean' ? consumer : consumer ( element ) ;
65
+ }
66
+
67
+ function shouldFilterAttribute (
68
+ element : Element ,
69
+ attribute : string ,
70
+ filter : DiffFilterSelector < Element > | undefined
71
+ ) : boolean {
72
+ if ( ! filter || ! filter . attributes || ! filter . attributes ! [ attribute ] ) {
73
+ return false ;
74
+ }
75
+ const consumer : DiffFilterConsumer < Element > = filter ! . attributes ! [ attribute ] ;
76
+
77
+ return typeof consumer === 'boolean' ? consumer : consumer ( element ) ;
78
+ }
79
+
17
80
/**
18
81
* Returns the description of the Element that differs.
19
82
*
@@ -32,8 +95,11 @@ function describe(element: Element): string {
32
95
*/
33
96
export function diffSclAttributes (
34
97
elementToBeCompared : Element ,
35
- elementToCompareAgainst : Element
98
+ elementToCompareAgainst : Element ,
99
+ filterToIgnore : DiffFilter < Element > ,
100
+ searchElementToBeCompared : Element ,
36
101
) : [ string , Diff < string > ] [ ] {
102
+
37
103
const attrDiffs : [ string , Diff < string > ] [ ] = [ ] ;
38
104
39
105
// First check if there is any text inside the element and there should be no child elements.
@@ -44,7 +110,18 @@ export function diffSclAttributes(
44
110
elementToCompareAgainst . childElementCount === 0 &&
45
111
newText !== oldText
46
112
) {
47
- attrDiffs . push ( [ 'value' , { newValue : newText , oldValue : oldText } ] ) ;
113
+ const shouldFilter : boolean = shouldFilterElement (
114
+ elementToBeCompared ,
115
+ getDiffFilterSelector (
116
+ elementToBeCompared ,
117
+ searchElementToBeCompared ,
118
+ filterToIgnore
119
+ )
120
+ ) ;
121
+
122
+ if ( ! shouldFilter ) {
123
+ attrDiffs . push ( [ 'value' , { newValue : newText , oldValue : oldText } ] ) ;
124
+ }
48
125
}
49
126
50
127
// Next check if there are any difference between attributes.
@@ -54,9 +131,19 @@ export function diffSclAttributes(
54
131
. concat ( elementToBeCompared . getAttributeNames ( ) )
55
132
) ;
56
133
for ( const name of attributeNames ) {
134
+ const shouldFilter : boolean = shouldFilterAttribute (
135
+ elementToBeCompared ,
136
+ name ,
137
+ getDiffFilterSelector (
138
+ elementToBeCompared ,
139
+ searchElementToBeCompared ,
140
+ filterToIgnore
141
+ )
142
+ ) ;
57
143
if (
144
+ ! shouldFilter &&
58
145
elementToCompareAgainst . getAttribute ( name ) !==
59
- elementToBeCompared . getAttribute ( name )
146
+ elementToBeCompared . getAttribute ( name )
60
147
) {
61
148
attrDiffs . push ( [
62
149
name ,
@@ -111,30 +198,54 @@ export function isSame(newValue: Element, oldValue: Element): boolean {
111
198
*/
112
199
export function diffSclChilds (
113
200
elementToBeCompared : Element ,
114
- elementToCompareAgainst : Element
201
+ elementToCompareAgainst : Element ,
202
+ filterToIgnore : DiffFilter < Element > ,
203
+ searchElementToBeCompared : Element ,
204
+ searchElementToCompareAgainst : Element
115
205
) : Diff < Element > [ ] {
116
206
const childDiffs : Diff < Element > [ ] = [ ] ;
117
207
const childrenToBeCompared = Array . from ( elementToBeCompared . children ) ;
118
208
const childrenToCompareTo = Array . from ( elementToCompareAgainst . children ) ;
119
209
120
210
childrenToBeCompared . forEach ( newElement => {
121
211
if ( ! newElement . closest ( 'Private' ) ) {
122
- const twinIndex = childrenToCompareTo . findIndex ( ourChild =>
123
- isSame ( newElement , ourChild )
212
+ const shouldFilter : boolean = shouldFilterElement (
213
+ newElement ,
214
+ getDiffFilterSelector (
215
+ newElement ,
216
+ searchElementToBeCompared ,
217
+ filterToIgnore
218
+ )
124
219
) ;
125
- const oldElement = twinIndex > - 1 ? childrenToCompareTo [ twinIndex ] : null ;
220
+ if ( ! shouldFilter ) {
221
+ const twinIndex = childrenToCompareTo . findIndex ( ourChild =>
222
+ isSame ( newElement , ourChild )
223
+ ) ;
224
+ const oldElement =
225
+ twinIndex > - 1 ? childrenToCompareTo [ twinIndex ] : null ;
126
226
127
- if ( oldElement ) {
128
- childrenToCompareTo . splice ( twinIndex , 1 ) ;
129
- childDiffs . push ( { newValue : newElement , oldValue : oldElement } ) ;
130
- } else {
131
- childDiffs . push ( { newValue : newElement , oldValue : null } ) ;
227
+ if ( oldElement ) {
228
+ childrenToCompareTo . splice ( twinIndex , 1 ) ;
229
+ childDiffs . push ( { newValue : newElement , oldValue : oldElement } ) ;
230
+ } else {
231
+ childDiffs . push ( { newValue : newElement , oldValue : null } ) ;
232
+ }
132
233
}
133
234
}
134
235
} ) ;
135
236
childrenToCompareTo . forEach ( oldElement => {
136
237
if ( ! oldElement . closest ( 'Private' ) ) {
137
- childDiffs . push ( { newValue : null , oldValue : oldElement } ) ;
238
+ const shouldFilter : boolean = shouldFilterElement (
239
+ oldElement ,
240
+ getDiffFilterSelector (
241
+ oldElement ,
242
+ searchElementToCompareAgainst ,
243
+ filterToIgnore
244
+ )
245
+ ) ;
246
+ if ( ! shouldFilter ) {
247
+ childDiffs . push ( { newValue : null , oldValue : oldElement } ) ;
248
+ }
138
249
}
139
250
} ) ;
140
251
return childDiffs ;
@@ -149,23 +260,50 @@ export function diffSclChilds(
149
260
*/
150
261
export function renderDiff (
151
262
elementToBeCompared : Element ,
152
- elementToCompareAgainst : Element
263
+ elementToCompareAgainst : Element ,
264
+ filterToIgnore : DiffFilter < Element > = { }
265
+ ) : TemplateResult | null {
266
+ return renderDiffInternal (
267
+ elementToBeCompared ,
268
+ elementToCompareAgainst ,
269
+ filterToIgnore ,
270
+ elementToBeCompared ,
271
+ elementToCompareAgainst
272
+ ) ;
273
+ }
274
+
275
+ function renderDiffInternal (
276
+ elementToBeCompared : Element ,
277
+ elementToCompareAgainst : Element ,
278
+ filterToIgnore : DiffFilter < Element > = { } ,
279
+ searchElementToBeCompared : Element ,
280
+ searchElementToCompareAgainst : Element
153
281
) : TemplateResult | null {
154
282
// Determine the ID from the current tag. These can be numbers or strings.
155
283
let idTitle : string | undefined = identity ( elementToBeCompared ) . toString ( ) ;
156
284
if ( idTitle === 'NaN' ) {
157
285
idTitle = undefined ;
158
286
}
159
287
160
- // First get all differences in attributes and text for the current 2 elements.
288
+ // Set the root elements if they are not defined yet
289
+ searchElementToBeCompared = searchElementToBeCompared || elementToBeCompared ;
290
+ searchElementToCompareAgainst =
291
+ searchElementToCompareAgainst || elementToCompareAgainst ;
292
+
161
293
const attrDiffs : [ string , Diff < string > ] [ ] = diffSclAttributes (
162
294
elementToBeCompared ,
163
- elementToCompareAgainst
295
+ elementToCompareAgainst ,
296
+ filterToIgnore ,
297
+ searchElementToBeCompared ,
164
298
) ;
299
+
165
300
// Next check which elements are added, deleted or in both elements.
166
301
const childDiffs : Diff < Element > [ ] = diffSclChilds (
167
302
elementToBeCompared ,
168
- elementToCompareAgainst
303
+ elementToCompareAgainst ,
304
+ filterToIgnore ,
305
+ searchElementToBeCompared ,
306
+ searchElementToCompareAgainst
169
307
) ;
170
308
171
309
const childAddedOrDeleted : Diff < Element > [ ] = [ ] ;
@@ -180,7 +318,15 @@ export function renderDiff(
180
318
181
319
// These children exist in both old and new element, let's check if there are any difference in the children.
182
320
const childToCompareTemplates = childToCompare
183
- . map ( diff => renderDiff ( diff . newValue ! , diff . oldValue ! ) )
321
+ . map ( diff =>
322
+ renderDiffInternal (
323
+ diff . newValue ! ,
324
+ diff . oldValue ! ,
325
+ filterToIgnore ,
326
+ searchElementToBeCompared ,
327
+ searchElementToCompareAgainst
328
+ )
329
+ )
184
330
. filter ( result => result !== null ) ;
185
331
186
332
// If there are difference generate the HTML otherwise just return null.
@@ -189,21 +335,26 @@ export function renderDiff(
189
335
attrDiffs . length > 0 ||
190
336
childAddedOrDeleted . length > 0
191
337
) {
192
- return html ` ${ attrDiffs . length > 0 || childAddedOrDeleted . length > 0
193
- ? html ` < mwc-list multi >
194
- ${ attrDiffs . length > 0
195
- ? html ` < mwc-list-item noninteractive ?twoline =${ ! ! idTitle } >
338
+ return html ` ${
339
+ attrDiffs . length > 0 || childAddedOrDeleted . length > 0
340
+ ? html ` < mwc-list multi >
341
+ ${
342
+ attrDiffs . length > 0
343
+ ? html ` < mwc-list-item noninteractive ?twoline =${ ! ! idTitle } >
196
344
< span class ="resultTitle ">
197
345
${ translate ( 'compare.attributes' , {
198
346
elementName : elementToBeCompared . tagName ,
199
347
} ) }
200
348
</ span >
201
- ${ idTitle
202
- ? html `< span slot ="secondary "> ${ idTitle } </ span > `
203
- : nothing }
349
+ ${
350
+ idTitle
351
+ ? html `< span slot ="secondary "> ${ idTitle } </ span > `
352
+ : nothing
353
+ }
204
354
</ mwc-list-item >
205
355
< li padded divider role ="separator "> </ li > `
206
- : '' }
356
+ : ''
357
+ }
207
358
${ repeat (
208
359
attrDiffs ,
209
360
e => e ,
@@ -220,38 +371,45 @@ export function renderDiff(
220
371
</ mwc-icon >
221
372
</ mwc-list-item > `
222
373
) }
223
- ${ childAddedOrDeleted . length > 0
224
- ? html ` < mwc-list-item noninteractive ?twoline =${ ! ! idTitle } >
374
+ ${
375
+ childAddedOrDeleted . length > 0
376
+ ? html ` < mwc-list-item noninteractive ?twoline =${ ! ! idTitle } >
225
377
< span class ="resultTitle ">
226
378
${ translate ( 'compare.children' , {
227
379
elementName : elementToBeCompared . tagName ,
228
380
} ) }
229
381
</ span >
230
- ${ idTitle
231
- ? html `< span slot ="secondary "> ${ idTitle } </ span > `
232
- : nothing }
382
+ ${
383
+ idTitle
384
+ ? html `< span slot ="secondary "> ${ idTitle } </ span > `
385
+ : nothing
386
+ }
233
387
</ mwc-list-item >
234
388
< li padded divider role ="separator "> </ li > `
235
- : '' }
389
+ : ''
390
+ }
236
391
${ repeat (
237
392
childAddedOrDeleted ,
238
393
e => e ,
239
394
diff =>
240
395
html ` < mwc-list-item twoline left hasMeta >
241
396
< span > ${ diff . oldValue ?. tagName ?? diff . newValue ?. tagName } </ span >
242
397
< span slot ="secondary ">
243
- ${ diff . oldValue
244
- ? describe ( diff . oldValue )
245
- : describe ( diff . newValue ) }
398
+ ${
399
+ diff . oldValue
400
+ ? describe ( diff . oldValue )
401
+ : describe ( diff . newValue )
402
+ }
246
403
</ span >
247
404
< mwc-icon slot ="meta ">
248
405
${ diff . oldValue ? 'delete' : 'add' }
249
406
</ mwc-icon >
250
407
</ mwc-list-item > `
251
408
) }
252
409
</ mwc-list > `
253
- : '' }
410
+ : ''
411
+ }
254
412
${ childToCompareTemplates } ` ;
255
413
}
256
414
return null ;
257
- }
415
+ }
0 commit comments