Skip to content

Commit f70f49a

Browse files
authored
fix(popover-edit): hover content not showing up if content changes after init (#18937)
If the table's content changes after the first render, the popover edit wasn't showing up on hover, because some of the new subscribers weren't getting the previous value. These changes fix the issue by using `shareReplay` instead of `share`. Fixes #18934.
1 parent 1c8a2c9 commit f70f49a

File tree

2 files changed

+56
-15
lines changed

2 files changed

+56
-15
lines changed

src/cdk-experimental/popover-edit/edit-event-dispatcher.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ import {
1515
distinctUntilChanged,
1616
filter,
1717
map,
18-
share,
1918
skip,
2019
startWith,
20+
shareReplay,
2121
} from 'rxjs/operators';
2222

2323
import {CELL_SELECTOR, ROW_SELECTOR} from './constants';
@@ -33,7 +33,7 @@ const FOCUS_DELAY = 0;
3333
/**
3434
* The possible states for hover content:
3535
* OFF - Not rendered.
36-
* FOCUSABLE - Rendered in the dom and stylyed for its contents to be focusable but invisible.
36+
* FOCUSABLE - Rendered in the dom and styled for its contents to be focusable but invisible.
3737
* ON - Rendered and fully visible.
3838
*/
3939
export const enum HoverContentState {
@@ -81,7 +81,7 @@ export class EditEventDispatcher {
8181
private readonly _startWithNull = startWith<Element|null>(null);
8282
private readonly _distinctShare = pipe(
8383
this._distinctUntilChanged as MonoTypeOperatorFunction<HoverContentState>,
84-
share(),
84+
shareReplay(1),
8585
);
8686
private readonly _startWithNullDistinct = pipe(
8787
this._startWithNull,
@@ -90,7 +90,7 @@ export class EditEventDispatcher {
9090

9191
readonly editingAndEnabled = this.editing.pipe(
9292
filter(cell => cell == null || !this.disabledCells.has(cell)),
93-
share(),
93+
shareReplay(1),
9494
);
9595

9696
/** An observable that emits the row containing focus or an active edit. */
@@ -105,7 +105,7 @@ export class EditEventDispatcher {
105105
this._distinctUntilChanged as MonoTypeOperatorFunction<Element|null>,
106106
auditTime(FOCUS_DELAY), // Use audit to skip over blur events to the next focused element.
107107
this._distinctUntilChanged as MonoTypeOperatorFunction<Element|null>,
108-
share(),
108+
shareReplay(1),
109109
);
110110

111111
/** Tracks rows that contain hover content with a reference count. */
@@ -132,16 +132,16 @@ export class EditEventDispatcher {
132132
skip(1), // Skip the initial emission of [null, null, null, null].
133133
map(computeHoverContentState),
134134
distinctUntilChanged(areMapEntriesEqual),
135-
// Optimization: Enter the zone before share() so that we trigger a single
135+
// Optimization: Enter the zone before shareReplay so that we trigger a single
136136
// ApplicationRef.tick for all row updates.
137137
this._enterZone(),
138-
share(),
138+
shareReplay(1),
139139
);
140140

141141
private readonly _editingAndEnabledDistinct = this.editingAndEnabled.pipe(
142142
distinctUntilChanged(),
143143
this._enterZone(),
144-
share(),
144+
shareReplay(1),
145145
);
146146

147147
// Optimization: Share row events observable with subsequent callers.

src/cdk-experimental/popover-edit/popover-edit.spec.ts

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -70,13 +70,18 @@ abstract class BaseTestComponent {
7070
@ViewChild('table') table: ElementRef;
7171

7272
preservedValues = new FormValueContainer<PeriodicElement, {'name': string}>();
73-
7473
nameEditDisabled = false;
7574
ignoreSubmitUnlessValid = true;
7675
clickOutBehavior: PopoverEditClickOutBehavior = 'close';
7776
colspan: CdkPopoverEditColspan = {};
7877
direction: Direction = 'ltr';
7978

79+
constructor() {
80+
this.renderData();
81+
}
82+
83+
abstract renderData(): void;
84+
8085
onSubmit(element: PeriodicElement, form: NgForm) {
8186
if (!form.valid) { return; }
8287

@@ -202,7 +207,11 @@ abstract class BaseTestComponent {
202207
`
203208
})
204209
class VanillaTableOutOfCell extends BaseTestComponent {
205-
elements = createElementData();
210+
elements: ChemicalElement[];
211+
212+
renderData() {
213+
this.elements = createElementData();
214+
}
206215
}
207216

208217
@Component({
@@ -231,7 +240,11 @@ class VanillaTableOutOfCell extends BaseTestComponent {
231240
`
232241
})
233242
class VanillaTableInCell extends BaseTestComponent {
234-
elements = createElementData();
243+
elements: ChemicalElement[];
244+
245+
renderData() {
246+
this.elements = createElementData();
247+
}
235248
}
236249

237250
class ElementDataSource extends DataSource<PeriodicElement> {
@@ -289,7 +302,11 @@ class ElementDataSource extends DataSource<PeriodicElement> {
289302
})
290303
class CdkFlexTableInCell extends BaseTestComponent {
291304
displayedColumns = ['before', 'name', 'weight'];
292-
dataSource = new ElementDataSource();
305+
dataSource: ElementDataSource;
306+
307+
renderData() {
308+
this.dataSource = new ElementDataSource();
309+
}
293310
}
294311

295312
@Component({
@@ -335,7 +352,11 @@ class CdkFlexTableInCell extends BaseTestComponent {
335352
})
336353
class CdkTableInCell extends BaseTestComponent {
337354
displayedColumns = ['before', 'name', 'weight'];
338-
dataSource = new ElementDataSource();
355+
dataSource: ElementDataSource;
356+
357+
renderData() {
358+
this.dataSource = new ElementDataSource();
359+
}
339360
}
340361

341362
const testCases: ReadonlyArray<[Type<BaseTestComponent>, string]> = [
@@ -369,7 +390,7 @@ describe('CDK Popover Edit', () => {
369390
afterEach(() => {
370391
// The overlay container's `ngOnDestroy` won't be called between test runs so we need
371392
// to call it ourselves, in order to avoid leaking containers between tests and potentially
372-
// throwing `querySelector` calls.
393+
// throwing off `querySelector` calls.
373394
overlayContainer.ngOnDestroy();
374395
});
375396

@@ -411,6 +432,21 @@ describe('CDK Popover Edit', () => {
411432
expect(component.hoverContentStateForRow(1)).toBe(HoverContentState.ON);
412433
}));
413434

435+
it('shows the hover content if the data changes after initialization', fakeAsync(() => {
436+
fixture.componentInstance.renderData();
437+
fixture.detectChanges();
438+
439+
const row = component.getRows()[0];
440+
row.dispatchEvent(new Event('mouseover', {bubbles: true}));
441+
row.dispatchEvent(new Event('mousemove', {bubbles: true}));
442+
443+
tick(20);
444+
row.dispatchEvent(new Event('mousemove', {bubbles: true}));
445+
tick(50);
446+
447+
expect(component.hoverContentStateForRow(0)).toBe(HoverContentState.ON);
448+
}));
449+
414450
it('shows hover content for the focused row and makes the rows above and below focusable',
415451
fakeAsync(() => {
416452
expect(component.hoverContentStateForRow(1)).toBe(HoverContentState.OFF);
@@ -930,7 +966,12 @@ cdkPopoverEditTabOut`, fakeAsync(() => {
930966
}
931967
});
932968

933-
function createElementData() {
969+
interface ChemicalElement {
970+
name: string;
971+
weight: number;
972+
}
973+
974+
function createElementData(): ChemicalElement[] {
934975
return [
935976
{name: 'Hydrogen', weight: 1.007},
936977
{name: 'Helium', weight: 4.0026},

0 commit comments

Comments
 (0)