Skip to content

Commit

Permalink
fix(addon-mobile): DropdownMobile is not compatible with `DropdownH…
Browse files Browse the repository at this point in the history
…over` (#9736)
  • Loading branch information
nsbarsukov authored Nov 14, 2024
1 parent 91af99f commit b57149a
Show file tree
Hide file tree
Showing 7 changed files with 188 additions and 1 deletion.
35 changes: 34 additions & 1 deletion projects/core/directives/dropdown/dropdown-hover.directive.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {DOCUMENT} from '@angular/common';
import {ContentChild, Directive, ElementRef, inject, Input} from '@angular/core';
import {toObservable} from '@angular/core/rxjs-interop';
import {TuiActiveZone} from '@taiga-ui/cdk/directives/active-zone';
import {tuiTypedFromEvent, tuiZoneOptimized} from '@taiga-ui/cdk/observables';
import {
Expand All @@ -8,8 +9,22 @@ import {
tuiIsElement,
} from '@taiga-ui/cdk/utils/dom';
import {tuiAsDriver, TuiDriver} from '@taiga-ui/core/classes';
import {delay, distinctUntilChanged, map, merge, of, share, switchMap, tap} from 'rxjs';
import {
delay,
distinctUntilChanged,
filter,
fromEvent,
map,
merge,
of,
share,
startWith,
switchMap,
takeUntil,
tap,
} from 'rxjs';

import {TuiDropdownDirective} from './dropdown.directive';
import {TUI_DROPDOWN_HOVER_OPTIONS} from './dropdown-hover.options';
import {TuiDropdownOpen} from './dropdown-open.directive';

Expand All @@ -30,7 +45,25 @@ export class TuiDropdownHover extends TuiDriver {
private readonly options = inject(TUI_DROPDOWN_HOVER_OPTIONS);
private readonly activeZone = inject(TuiActiveZone);
private readonly open = inject(TuiDropdownOpen, {optional: true});
/**
* Dropdown can be removed not only via click/touch –
* swipe on mobile devices removes dropdown sheet without triggering new mouseover / mouseout events.
*/
private readonly dropdownExternalRemoval$ = toObservable(
inject(TuiDropdownDirective).ref,
).pipe(filter((x) => !x && this.hovered));

private readonly stream$ = merge(
this.dropdownExternalRemoval$.pipe(
switchMap(() =>
tuiTypedFromEvent(this.doc, 'pointerdown').pipe(
map(tuiGetActualTarget),
delay(this.hideDelay),
startWith(null),
takeUntil(fromEvent(this.doc, 'mouseover')),
),
),
),
tuiTypedFromEvent(this.doc, 'mouseover').pipe(map(tuiGetActualTarget)),
tuiTypedFromEvent(this.doc, 'mouseout').pipe(map((e) => e.relatedTarget)),
).pipe(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import {DemoRoute} from '@demo/routes';
import {
TuiDocumentationPagePO,
tuiGoto,
TuiMobileDropdownPO,
} from '@demo-playwright/utils';
import type {Locator} from '@playwright/test';
import {expect, test} from '@playwright/test';

import {
TUI_PLAYWRIGHT_MOBILE_USER_AGENT,
TUI_PLAYWRIGHT_MOBILE_VIEWPORT_HEIGHT,
TUI_PLAYWRIGHT_MOBILE_VIEWPORT_WIDTH,
} from '../../../playwright.options';

const {describe, beforeEach} = test;

test.describe('DropdownHover', () => {
describe('Examples', () => {
beforeEach(async ({page}) => {
await tuiGoto(page, DemoRoute.DropdownHover);
});

describe('With DropdownMobile', () => {
let example!: Locator;
let mobileCalendar: TuiMobileDropdownPO;

test.use({
viewport: {
width: TUI_PLAYWRIGHT_MOBILE_VIEWPORT_WIDTH,
height: TUI_PLAYWRIGHT_MOBILE_VIEWPORT_HEIGHT,
},
userAgent: TUI_PLAYWRIGHT_MOBILE_USER_AGENT,
isMobile: true,
});

beforeEach(({page}) => {
example = new TuiDocumentationPagePO(page).getExample('#dropdown-mobile');
mobileCalendar = new TuiMobileDropdownPO(
page.locator('tui-dropdown-mobile'),
);
});

test('Opens mobile version of dropdown on the 1st time click', async ({
page,
}) => {
await example.locator('button').click();

await expect(page.locator('tui-dropdown')).not.toBeAttached();
await expect(page.locator('tui-dropdown-mobile')).toBeVisible();
await expect(page).toHaveScreenshot(
'mobile-dropdown-1st-time-time-click.png',
);
});

test('Closes dropdown on click on overlay', async ({page}) => {
await example.locator('button').click();

await expect(page.locator('tui-dropdown-mobile')).toBeVisible();

await mobileCalendar.overlay.click();

await expect(page.locator('tui-dropdown-mobile')).not.toBeAttached();
});

test('Opens mobile version of dropdown on the 2nd time click', async ({
page,
}) => {
await example.locator('button').click();
await mobileCalendar.overlay.click();
await example.locator('button').click();

await expect(page.locator('tui-dropdown-mobile')).toBeVisible();

await expect(page).toHaveScreenshot(
'mobile-dropdown-2nd-time-time-click.png',
);
});
});
});
});
1 change: 1 addition & 0 deletions projects/demo-playwright/utils/page-objects/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export * from './input-slider.po';
export * from './input-tag.po';
export * from './input-time.po';
export * from './mobile-calendar.po';
export * from './mobile-dropdown.po';
export * from './multi-select.po';
export * from './range.po';
export * from './select.po';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type {Locator} from '@playwright/test';

export class TuiMobileDropdownPO {
public overlay = this.host.locator('.t-filler');

constructor(private readonly host: Locator) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<button
appearance="outline-grayscale"
iconStart="@tui.ellipsis"
tuiDropdownHover
tuiDropdownMobile="Contact Us"
tuiIconButton
type="button"
[tuiDropdown]="dropdown"
>
More
</button>

<ng-template #dropdown>
<tui-data-list>
<a
href="https://github.com/taiga-family/taiga-ui"
iconStart="assets/images/github.svg"
rel="noreferrer"
target="_blank"
tuiOption
>
GitHub
</a>
<a
href="https://t.me/taiga_ui"
iconStart="assets/icons/telegram.svg"
rel="noreferrer"
target="_blank"
tuiOption
>
Telegram
</a>
<a
href="https://discord.gg/zrB2EdJjEy"
iconStart="assets/icons/discord.svg"
rel="noreferrer"
target="_blank"
tuiOption
>
Discord
</a>
</tui-data-list>
</ng-template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {Component} from '@angular/core';
import {changeDetection} from '@demo/emulate/change-detection';
import {encapsulation} from '@demo/emulate/encapsulation';
import {TuiDropdownMobile} from '@taiga-ui/addon-mobile';
import {TuiButton, TuiDataList, TuiDropdown} from '@taiga-ui/core';

@Component({
standalone: true,
imports: [TuiButton, TuiDataList, TuiDropdown, TuiDropdownMobile],
templateUrl: './index.html',
styles: ['[tuiOption] {justify-content: flex-start}'],
encapsulation,
changeDetection,
})
export default class Example {}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@
[component]="4 | tuiComponent"
[content]="4 | tuiExample: 'html,ts'"
/>

<tui-doc-example
id="dropdown-mobile"
heading="With DropdownMobile"
[component]="5 | tuiComponent"
[content]="5 | tuiExample: 'html,ts'"
/>
</ng-template>

<ng-template pageTab>
Expand Down

0 comments on commit b57149a

Please sign in to comment.