Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Modal: Optimize content scroll listening in modal #3163

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
5ef3d83
Disable event listener on desktop
AgreSanGit Jul 7, 2023
ccf581e
Add unfinished test for IonScroll
AgreSanGit Jul 10, 2023
3a6de56
Refactor tests
mark-drastrup Jul 12, 2023
0326631
Merge branch 'develop' into enhancement/3044-optimize-content-scroll-…
AgreSanGit Jul 18, 2023
c6d9b8f
Merge branch 'develop' into enhancement/3044-optimize-content-scroll-…
AgreSanGit Jul 25, 2023
4058172
Dynamically check resize and destroy contentScroll
AgreSanGit Aug 4, 2023
40b75d1
Add describe block to top level describe
AgreSanGit Aug 31, 2023
fa17787
Merge branch 'develop' into enhancement/3044-optimize-content-scroll-…
AgreSanGit Aug 31, 2023
d656467
Member variable for scrollEvents
AgreSanGit Oct 12, 2023
ed4079f
Change member variable name
AgreSanGit Oct 12, 2023
ddbc1d3
intializeContentScrollListening once on mobile and set flag
AgreSanGit Oct 13, 2023
59e3ef3
Make isContentScrolled more readable
AgreSanGit Oct 13, 2023
a12674b
Remove imports from test
AgreSanGit Oct 13, 2023
65cf377
Add test for scrollEventsEnabled when initialized
AgreSanGit Oct 19, 2023
51acf85
Merge branch 'develop' into enhancement/3044-optimize-content-scroll-…
AgreSanGit Oct 26, 2023
b33ae38
Add test for resize from desktop to phone
AgreSanGit Oct 27, 2023
85b7106
Fix duplicates of it
AgreSanGit Oct 27, 2023
673cc43
Merge branch 'develop' into enhancement/3044-optimize-content-scroll-…
AgreSanGit Nov 16, 2023
e8e3b02
Add test suite for borderbottomcolor on phone scroll
AgreSanGit Nov 24, 2023
e70c9bb
Remove unnecessary whenstable
AgreSanGit Nov 24, 2023
775c6c3
Merge branch 'develop' into enhancement/3044-optimize-content-scroll-…
AgreSanGit Nov 24, 2023
2fb9cc4
Using toHaveComputedStyle
AgreSanGit Nov 24, 2023
f058cf2
Merge branch 'enhancement/3044-optimize-content-scroll-listening-in-m…
AgreSanGit Nov 24, 2023
f17b6f3
Update libs/designsystem/modal/src/modal-wrapper/modal-wrapper.compon…
AgreSanGit Dec 14, 2023
9875724
Merge branch 'develop' into enhancement/3044-optimize-content-scroll-…
AgreSanGit Dec 14, 2023
1afb7e2
Improve readability for border-bottom test
AgreSanGit Dec 14, 2023
d1c2722
Transition none
AgreSanGit Dec 15, 2023
b722d9e
test: align code
jakobe Dec 18, 2023
cdd75ff
Merge branch 'develop' into enhancement/3044-optimize-content-scroll-…
jakobe Dec 18, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
</ion-toolbar>
</ion-header>

<ion-content [scrollEvents]="true">
<ion-content [scrollEvents]="scrollEventsEnabled">
<ion-header *ngIf="_hasCollapsibleTitle" collapse="condense">
<ion-toolbar>
<span class="kirby-text-xlarge" #contentTitle></span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { ButtonComponent } from '@kirbydesign/designsystem/button';
import { PageModule } from '@kirbydesign/designsystem/page';
import { CanDismissHelper, ModalWrapperComponent } from '@kirbydesign/designsystem/modal';
import {
DummyContentEmbeddedComponent,
DynamicFooterEmbeddedComponent,
DynamicPageProgressEmbeddedComponent,
InputEmbeddedComponent,
Expand All @@ -21,6 +22,8 @@ import {
TitleEmbeddedComponent,
} from './modal-wrapper.testbuilder';

const { getColor } = DesignTokenHelper;

describe('ModalWrapperComponent', () => {
const createComponent = createComponentFactory({
component: ModalWrapperComponent,
Expand Down Expand Up @@ -648,4 +651,78 @@ describe('ModalWrapperComponent', () => {
});
});
});

describe('listenForScroll', () => {
afterEach(() => {
TestHelper.resetTestWindow();
});
it('should set scrollEventsEnabled to be false when opened on desktop', async () => {
await TestHelper.resizeTestWindow(TestHelper.screensize.desktop);

modalWrapperTestBuilder = new ModalWrapperTestBuilder(createComponent);
spectator = modalWrapperTestBuilder
.flavor('modal')
.title('test')
.component(TitleEmbeddedComponent)
.build();

expect(spectator.component.scrollEventsEnabled).toBeFalse();
});

it('should set scrollEventsEnabled to be true when opened on a phone', async () => {
await TestHelper.resizeTestWindow(TestHelper.screensize.phone);

modalWrapperTestBuilder = new ModalWrapperTestBuilder(createComponent);
spectator = modalWrapperTestBuilder
.flavor('modal')
.title('test')
.component(TitleEmbeddedComponent)
.build();

expect(spectator.component.scrollEventsEnabled).toBeTrue();
});

it('should set scrollEventsEnabled to be true when resizing from desktop to phone', async () => {
await TestHelper.resizeTestWindow(TestHelper.screensize.desktop);
modalWrapperTestBuilder = new ModalWrapperTestBuilder(createComponent);
spectator = modalWrapperTestBuilder
.flavor('modal')
.title('test')
.component(TitleEmbeddedComponent)
.build();
expect(spectator.component.scrollEventsEnabled).toBeFalse();

await TestHelper.resizeTestWindow(TestHelper.screensize.phone);
await TestHelper.whenTrue(() => spectator.component.scrollEventsEnabled);

expect(spectator.component.scrollEventsEnabled).toBeTrue();
});

it('should set border-bottom-color on ion-toolbar when scrolling on phone to the bottom or past offset', async () => {
await TestHelper.resizeTestWindow(TestHelper.screensize.phone);
modalWrapperTestBuilder = new ModalWrapperTestBuilder(createComponent);
spectator = modalWrapperTestBuilder
.flavor('modal')
.component(DummyContentEmbeddedComponent)
.build();
await spectator.fixture.whenStable();
const MinimumScrollableContentHeight = '500px';
const ionContentElement = spectator.query('ion-content') as HTMLElement;
ionContentElement.style.minHeight = MinimumScrollableContentHeight;
spectator.detectChanges();

spectator.component.scrollToBottom();
await spectator.fixture.whenStable();
spectator.detectChanges();
await TestHelper.whenTrue(() => spectator.component.isContentScrolled);
const ionToolbarInScrolled = document.querySelector(
'ion-header.content-scrolled ion-toolbar'
) as HTMLElement;
ionToolbarInScrolled.style.transition = 'none';

expect(ionToolbarInScrolled).toHaveComputedStyle({
'border-bottom-color': getColor('medium'),
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -133,12 +133,10 @@ export class ModalWrapperComponent
}
return this._intersectionObserver;
}

scrollEventsEnabled: boolean;
isContentScrolled: boolean;
private contentScrolled$: Observable<ScrollDetail>;

private destroy$: Subject<void> = new Subject<void>();

@HostBinding('class.drawer')
get _isDrawer() {
return this.config.flavor === 'drawer';
Expand Down Expand Up @@ -170,6 +168,7 @@ export class ModalWrapperComponent
this.initializeModalRoute();
this.listenForIonModalDidPresent();
this.listenForIonModalWillDismiss();
this.listenForScroll();
this.initializeResizeModalToModalWrapper();
this.componentPropsInjector = Injector.create({
providers: [{ provide: COMPONENT_PROPS, useValue: this.config.componentProps }],
Expand All @@ -181,8 +180,6 @@ export class ModalWrapperComponent
if (this.toolbarButtonsQuery) {
this.toolbarButtons = this.toolbarButtonsQuery.map((buttonRef) => buttonRef.nativeElement);
}

this.initializeContentScrollListening();
}

private _currentFooter: HTMLElement | null = null;
Expand Down Expand Up @@ -343,24 +340,35 @@ export class ModalWrapperComponent
}
}

/*
* Runs scroll subscription outside zone to avoid excessive amount of CD cycles
* when ionScroll emits.
*/
private initializeContentScrollListening() {
this.zone.runOutsideAngular(() => {
this.contentScrolled$ = this.ionContent.ionScroll.pipe(
debounceTime(contentScrollDebounceTimeInMS),
map((event) => event.detail),
takeUntil(this.destroy$)
);
private listenForScroll() {
const query = `(min-width: ${DesignTokenHelper.breakpoints.medium})`;
const mediaQuery = this.windowRef.nativeWindow.matchMedia(query);
const enableScrollEvents = (listOrEvent: MediaQueryList | MediaQueryListEvent) => {
const isDesktop = listOrEvent.matches;
this.scrollEventsEnabled = !isDesktop;
};

this.contentScrolled$.subscribe((scrollInfo: ScrollDetail) => {
if (scrollInfo.scrollTop > contentScrolledOffsetInPixels !== this.isContentScrolled) {
this.isContentScrolled = !this.isContentScrolled;
this.changeDetector.detectChanges();
}
});
enableScrollEvents(mediaQuery);
mediaQuery.onchange = enableScrollEvents;

// Runs scroll subscription outside zone to avoid excessive amount of CD cycles
// when ionScroll emits.
this.zone.runOutsideAngular(() => {
// Always subscribe as ionScroll only emits when scrollEventsEnabled is true
this.ionContent.ionScroll
.pipe(
debounceTime(contentScrollDebounceTimeInMS),
map((event) => event.detail),
takeUntil(this.destroy$)
)
.subscribe((scrollInfo: ScrollDetail) => {
const contentScrolledPastOffset = scrollInfo.scrollTop > contentScrolledOffsetInPixels;

if (contentScrolledPastOffset !== this.isContentScrolled) {
this.isContentScrolled = contentScrolledPastOffset;
this.changeDetector.detectChanges();
}
});
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,49 @@ export class TitleEmbeddedComponent {
this._title = title;
}
}

@Component({
template: `
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Adipisci animi aperiam deserunt
dolore error esse laborum magni natus nihil optio perferendis placeat, quae sed, sequi sunt
totam voluptatem! Dicta, quaerat!
</p>
<p>
Aut, dignissimos dolorum ducimus et rem reprehenderit rerum sunt ut! Ad aliquid beatae cum
esse et eveniet facere natus numquam obcaecati qui quia quisquam quo repellat repudiandae sit,
soluta voluptatibus!
</p>
<p>
Aspernatur dolore enim incidunt libero molestiae nostrum quasi? Accusamus aut culpa dolores
doloribus laborum nesciunt voluptates! Consectetur cumque doloremque eius esse et excepturi
hic, inventore mollitia nisi, reiciendis, tempora unde!
</p>
<p>
Blanditiis, cupiditate distinctio earum illo impedit laborum velit veritatis. Accusamus
adipisci alias aperiam, assumenda corporis culpa cum debitis exercitationem impedit laborum
possimus quam qui repellat, saepe similique sint soluta. Unde.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Adipisci animi aperiam deserunt
dolore error esse laborum magni natus nihil optio perferendis placeat, quae sed, sequi sunt
totam voluptatem! Dicta, quaerat!
</p>
<p>
Aut, dignissimos dolorum ducimus et rem reprehenderit rerum sunt ut! Ad aliquid beatae cum
esse et eveniet facere natus numquam obcaecati qui quia quisquam quo repellat repudiandae sit,
soluta voluptatibus!
</p>
<p>
Aspernatur dolore enim incidunt libero molestiae nostrum quasi? Accusamus aut culpa dolores
doloribus laborum nesciunt voluptates! Consectetur cumque doloremque eius esse et excepturi
hic, inventore mollitia nisi, reiciendis, tempora unde!
</p>
<p>
Blanditiis, cupiditate distinctio earum illo impedit laborum velit veritatis. Accusamus
adipisci alias aperiam, assumenda corporis culpa cum debitis exercitationem impedit laborum
possimus quam qui repellat, saepe similique sint soluta. Unde.
</p>
`,
})
export class DummyContentEmbeddedComponent {}
Loading