@@ -141,6 +141,217 @@ function tests(context, compare) {
141141 return compare ( page , `${ context } -datagrid-filters-work.txt` ) ;
142142 } ) ;
143143
144+ test ( "Filter tombstone removes a programmatically applied filter from slave viewers" , async ( {
145+ page,
146+ } ) => {
147+ const config = {
148+ viewers : {
149+ One : {
150+ table : "superstore" ,
151+ name : "Test" ,
152+ group_by : [ "State" ] ,
153+ columns : [ "Sales" ] ,
154+ plugin : "Datagrid" ,
155+ } ,
156+ Two : { table : "superstore" , name : "One" } ,
157+ } ,
158+ master : {
159+ widgets : [ "One" ] ,
160+ } ,
161+ detail : {
162+ main : {
163+ currentIndex : 0 ,
164+ type : "tab-area" ,
165+ widgets : [ "Two" ] ,
166+ } ,
167+ } ,
168+ } ;
169+
170+ async function awaitConfigChange ( ) {
171+ return await page . evaluate ( async ( ) => {
172+ let resolve ;
173+ const timer = new Promise ( ( x ) => {
174+ resolve = x ;
175+ } ) ;
176+
177+ workspace . addEventListener ( "workspace-layout-update" , resolve ) ;
178+ await timer ;
179+ workspace . removeEventListener (
180+ "workspace-layout-update" ,
181+ resolve ,
182+ ) ;
183+
184+ return await workspace . save ( ) ;
185+ } ) ;
186+ }
187+
188+ await page . evaluate ( async ( config ) => {
189+ const workspace = document . getElementById ( "workspace" ) ;
190+ await workspace . restore ( config ) ;
191+ await workspace . flush ( ) ;
192+ } , config ) ;
193+
194+ // Apply a filter for "Category" via programmatic dispatch.
195+ // "Category" is not in the master's group_by/split_by/filter, so it
196+ // would not be cleared by the existing candidates mechanism.
197+ let cfgPromise = awaitConfigChange ( ) ;
198+ await page . evaluate ( async ( ) => {
199+ const masterViewer = document . querySelector (
200+ ".workspace-master-widget" ,
201+ ) ;
202+ masterViewer . dispatchEvent (
203+ new CustomEvent ( "perspective-select" , {
204+ bubbles : true ,
205+ composed : true ,
206+ detail : {
207+ selected : true ,
208+ row : { } ,
209+ column_names : [ "Category" ] ,
210+ config : { filter : [ [ "Category" , "==" , "Furniture" ] ] } ,
211+ } ,
212+ } ) ,
213+ ) ;
214+ } ) ;
215+
216+ let cfg = await cfgPromise ;
217+ expect ( cfg . viewers . Two . filter ) . toEqual ( [
218+ [ "Category" , "==" , "Furniture" ] ,
219+ ] ) ;
220+
221+ // Send a tombstone [["Category", undefined]] to explicitly clear
222+ // the Category filter from slave viewers.
223+ cfgPromise = awaitConfigChange ( ) ;
224+ await page . evaluate ( async ( ) => {
225+ const masterViewer = document . querySelector (
226+ ".workspace-master-widget" ,
227+ ) ;
228+ masterViewer . dispatchEvent (
229+ new CustomEvent ( "perspective-select" , {
230+ bubbles : true ,
231+ composed : true ,
232+ detail : {
233+ selected : true ,
234+ row : { } ,
235+ column_names : [ ] ,
236+ config : { filter : [ [ "Category" , undefined ] ] } ,
237+ } ,
238+ } ) ,
239+ ) ;
240+ } ) ;
241+
242+ cfg = await cfgPromise ;
243+ expect ( cfg . viewers . Two . filter ) . toEqual ( [ ] ) ;
244+ } ) ;
245+
246+ test ( "Filter tombstone preserves other slave filters while clearing targeted column" , async ( {
247+ page,
248+ } ) => {
249+ const config = {
250+ viewers : {
251+ One : {
252+ table : "superstore" ,
253+ name : "Test" ,
254+ group_by : [ "State" ] ,
255+ columns : [ "Sales" ] ,
256+ plugin : "Datagrid" ,
257+ } ,
258+ Two : { table : "superstore" , name : "One" } ,
259+ } ,
260+ master : {
261+ widgets : [ "One" ] ,
262+ } ,
263+ detail : {
264+ main : {
265+ currentIndex : 0 ,
266+ type : "tab-area" ,
267+ widgets : [ "Two" ] ,
268+ } ,
269+ } ,
270+ } ;
271+
272+ async function awaitConfigChange ( ) {
273+ return await page . evaluate ( async ( ) => {
274+ let resolve ;
275+ const timer = new Promise ( ( x ) => {
276+ resolve = x ;
277+ } ) ;
278+
279+ workspace . addEventListener ( "workspace-layout-update" , resolve ) ;
280+ await timer ;
281+ workspace . removeEventListener (
282+ "workspace-layout-update" ,
283+ resolve ,
284+ ) ;
285+
286+ return await workspace . save ( ) ;
287+ } ) ;
288+ }
289+
290+ await page . evaluate ( async ( config ) => {
291+ const workspace = document . getElementById ( "workspace" ) ;
292+ await workspace . restore ( config ) ;
293+ await workspace . flush ( ) ;
294+ } , config ) ;
295+
296+ // Apply filters for both "Category" and "Segment" via programmatic
297+ // dispatch.
298+ let cfgPromise = awaitConfigChange ( ) ;
299+ await page . evaluate ( async ( ) => {
300+ const masterViewer = document . querySelector (
301+ ".workspace-master-widget" ,
302+ ) ;
303+ masterViewer . dispatchEvent (
304+ new CustomEvent ( "perspective-select" , {
305+ bubbles : true ,
306+ composed : true ,
307+ detail : {
308+ selected : true ,
309+ row : { } ,
310+ column_names : [ "Category" , "Segment" ] ,
311+ config : {
312+ filter : [
313+ [ "Category" , "==" , "Furniture" ] ,
314+ [ "Segment" , "==" , "Consumer" ] ,
315+ ] ,
316+ } ,
317+ } ,
318+ } ) ,
319+ ) ;
320+ } ) ;
321+
322+ let cfg = await cfgPromise ;
323+ expect ( cfg . viewers . Two . filter ) . toEqual ( [
324+ [ "Category" , "==" , "Furniture" ] ,
325+ [ "Segment" , "==" , "Consumer" ] ,
326+ ] ) ;
327+
328+ // Send a tombstone only for "Category"; "Segment" filter should be
329+ // preserved.
330+ cfgPromise = awaitConfigChange ( ) ;
331+ await page . evaluate ( async ( ) => {
332+ const masterViewer = document . querySelector (
333+ ".workspace-master-widget" ,
334+ ) ;
335+ masterViewer . dispatchEvent (
336+ new CustomEvent ( "perspective-select" , {
337+ bubbles : true ,
338+ composed : true ,
339+ detail : {
340+ selected : true ,
341+ row : { } ,
342+ column_names : [ ] ,
343+ config : { filter : [ [ "Category" , undefined ] ] } ,
344+ } ,
345+ } ) ,
346+ ) ;
347+ } ) ;
348+
349+ cfg = await cfgPromise ;
350+ // Category is cleared, but Segment is still there because it was not
351+ // in candidates and was not tombstoned.
352+ expect ( cfg . viewers . Two . filter ) . toEqual ( [ [ "Segment" , "==" , "Consumer" ] ] ) ;
353+ } ) ;
354+
144355 test ( "Child classes of datagrid behave the same way" , async ( { page } ) => {
145356 const config = {
146357 viewers : {
0 commit comments