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

feat: Add flip middleware to popover and tooltip #329

Merged
merged 4 commits into from
Jan 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 0 additions & 7 deletions apps/docs-app/app/components/f/components/dropdown.gts
Original file line number Diff line number Diff line change
Expand Up @@ -116,13 +116,6 @@ export default class extends Component {
@value={{this.hasIcon}}
@onInput={{fn this.update "hasIcon"}}
/>
<Args.Bool
@name="isShown"
@defaultValue="undefined"
@description="Whether to open the dropdown"
@value={{this.isShown}}
@onInput={{fn this.update "isShown"}}
/>
<Args.Number
@name="offset"
@description="How far to offset the dropdown from the button (in pixels)"
Expand Down
26 changes: 13 additions & 13 deletions apps/docs-app/app/components/f/components/popover.gts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export default class extends Component {
arrow: boolean = true;

@tracked
isShown?: boolean;
flip: boolean = false;

@tracked
offset?: number;
Expand All @@ -62,7 +62,7 @@ export default class extends Component {
<Popover
@alignment={{this.alignment}}
@arrow={{this.arrow}}
@isShown={{this.isShown}}
@flip={{this.flip}}
@offset={{this.offset}}
@side={{this.side}}
@onShow={{fn this.toast.info "onShow was fired"}}
Expand Down Expand Up @@ -106,11 +106,11 @@ export default class extends Component {
@onInput={{fn this.update "arrow"}}
/>
<Args.Bool
@name="isShown"
@defaultValue="undefined"
@description="Whether to show the popover"
@value={{this.isShown}}
@onInput={{fn this.update "isShown"}}
@name="flip"
@defaultValue={{false}}
@description="Whether to flip the side the popover is on when it reaches the viewport boundary"
@value={{this.flip}}
@onInput={{fn this.update "flip"}}
/>
<Args.Number
@name="offset"
Expand Down Expand Up @@ -147,7 +147,7 @@ export default class extends Component {
<Popover
@alignment={{this.alignment}}
@arrow={{this.arrow}}
@isShown={{this.isShown}}
@flip={{this.flip}}
@offset={{this.offset}}
@side={{this.side}}
@onShow={{fn this.toast.info "onShow was fired"}}
Expand Down Expand Up @@ -187,11 +187,11 @@ export default class extends Component {
@onInput={{fn this.update "arrow"}}
/>
<Args.Bool
@name="isShown"
@defaultValue="undefined"
@description="Whether to show the popover"
@value={{this.isShown}}
@onInput={{fn this.update "isShown"}}
@name="flip"
@defaultValue={{false}}
@description="Whether to flip the side the popover is on when it reaches the viewport boundary"
@value={{this.flip}}
@onInput={{fn this.update "flip"}}
/>
<Args.Number
@name="offset"
Expand Down
11 changes: 11 additions & 0 deletions apps/docs-app/app/components/f/components/tooltip.gts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ export default class extends Component {
@tracked
alignment?: Alignment;

@tracked
flip?: boolean;

@tracked
offset?: number;

Expand All @@ -37,6 +40,7 @@ export default class extends Component {
<div class="p-2">
<Tooltip
@alignment={{this.alignment}}
@flip={{this.flip}}
@offset={{this.offset}}
@side={{this.side}}
@onShow={{fn this.toast.info "onShow was fired"}}
Expand Down Expand Up @@ -148,6 +152,13 @@ export default class extends Component {
@options={{array "" "start" "end"}}
@onInput={{fn this.update "alignment"}}
/>
<Args.Bool
@name="flip"
@defaultValue={{false}}
@description="Whether to flip the side the tooltip is on when it reaches the viewport boundary"
@value={{this.flip}}
@onInput={{fn this.update "flip"}}
/>
<Args.Number
@name="offset"
@description="How far to offset the tooltip from the target (in pixels)"
Expand Down
41 changes: 9 additions & 32 deletions apps/test-app/tests/integration/components/popover-test.gts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { render } from '@ember/test-helpers';
import { settled } from '@ember/test-helpers';
import { click } from '@ember/test-helpers';
import { click, render, settled } from '@ember/test-helpers';
import { tracked } from '@glimmer/tracking';
import Button from '@nrg-ui/core/components/button';
import Popover from '@nrg-ui/core/components/popover';
Expand Down Expand Up @@ -29,36 +27,16 @@ module('Integration | Component | popover', function (hooks) {
this.model = new Model();
});

test('`isShown` works', async function (this: TestContext, assert) {
const { model } = this;

await render(<template>
<Popover @isShown={{model.isShown}}>
<:content as |Content|>
<Content.Header>
header
</Content.Header>
<Content.Body>
body
</Content.Body>
</:content>
</Popover>
</template>);

assert.dom('.popover').hasClass('hidden');

this.model.isShown = true;

await settled();

assert.dom('.popover').doesNotHaveClass('hidden');
});

test('`direction` works', async function (this: TestContext, assert) {
const { model } = this;

await render(<template>
<Popover @isShown={{true}} @side={{model.side}}>
<Popover @side={{model.side}}>
<:control as |actions|>
<Button @onClick={{actions.toggle}}>
toggle
</Button>
</:control>
<:content as |Content|>
<Content.Header>
header
Expand All @@ -69,11 +47,10 @@ module('Integration | Component | popover', function (hooks) {
</:content>
</Popover>
</template>);

assert.dom('.popover').hasClass('bs-popover-bottom');

this.model.side = 'top';
await settled();
this.model.side = "top";
await click('button');
assert.dom('.popover').hasClass('bs-popover-top');

this.model.side = 'start';
Expand Down
1 change: 0 additions & 1 deletion packages/ember-core/src/components/dropdown.gts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ export interface DropdownSignature {
disabled?: boolean;
fullWidth?: boolean;
hasIcon?: boolean;
isShown?: boolean;
loading?: boolean;
offset?: string | number;
scrollable?: boolean;
Expand Down
37 changes: 25 additions & 12 deletions packages/ember-core/src/components/popover.gts
TSenter marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { concat, hash } from '@ember/helper';
import { arrow, computePosition, offset, size } from '@floating-ui/dom';
import { arrow, computePosition, flip, offset, size } from '@floating-ui/dom';
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';

Expand Down Expand Up @@ -53,8 +53,8 @@ export interface PopoverSignature {
alignment?: Alignment;
arrow?: boolean;
controlElement?: HTMLElement;
flip?: boolean;
fullWidth?: boolean;
isShown?: boolean;
offset?: string | number;
side?: Direction;

Expand Down Expand Up @@ -95,7 +95,10 @@ export default class Popover extends Component<PopoverSignature> {
id = `popover-${crypto.randomUUID()}`;

@tracked
_isShown = false;
isShown = false;

@tracked
adjustedSide: Direction = 'bottom';

get hasArrow() {
return this.args.arrow ?? true;
Expand All @@ -121,6 +124,10 @@ export default class Popover extends Component<PopoverSignature> {
get middleware() {
const middleware = [offset(this.offset)];

if (this.args.flip) {
middleware.push(flip());
}

if (this.hasArrow) {
middleware.push(arrow({ element: this.arrow! }));
}
Expand All @@ -144,10 +151,6 @@ export default class Popover extends Component<PopoverSignature> {
return this.args.side ?? 'bottom';
}

get isShown() {
return this.args.isShown ?? this._isShown;
}

get control() {
return this.args.controlElement ?? this._control;
}
Expand All @@ -161,17 +164,19 @@ export default class Popover extends Component<PopoverSignature> {
return;
}

this._isShown = true;
const { currentTarget } = evtOrInput as Event;
this.isShown = true;

await this.args.onShow?.();

if (evtOrInput instanceof HTMLInputElement) {
this._control = evtOrInput;
this.showPopover();
} else if (
evtOrInput instanceof Event &&
evtOrInput.currentTarget instanceof HTMLElement
currentTarget instanceof HTMLElement
) {
this._control = evtOrInput.currentTarget;
this._control = currentTarget;
this.showPopover();
}
};
Expand All @@ -181,7 +186,7 @@ export default class Popover extends Component<PopoverSignature> {
return;
}

this._isShown = false;
this.isShown = false;
await this.args.onHide?.();

this._control = null;
Expand Down Expand Up @@ -220,6 +225,14 @@ export default class Popover extends Component<PopoverSignature> {
return;
}

if (placement !== this.placement) {
this.adjustedSide = Object.keys(SIDE_TRANSLATION).find(
(side) => SIDE_TRANSLATION[side as Direction] === placement,
) as Direction;
} else {
this.adjustedSide = this.side;
}

const { x: arrowX, y: arrowY } = middlewareData.arrow!;
const staticSide: string =
ARROW_SIDE[placement.split('-')[0] as Direction]!;
Expand All @@ -245,7 +258,7 @@ export default class Popover extends Component<PopoverSignature> {
id={{this.id}}
class={{classes
(unless this.isShown "hidden")
(concat "popover bs-popover-" this.side)
(concat "popover bs-popover-" this.adjustedSide)
}}
{{onInsert this.initPopover}}
{{! @glint-expect-error Modifier types are currently not correct }}
Expand Down
3 changes: 2 additions & 1 deletion packages/ember-core/src/components/tooltip.gts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export interface TooltipSignature {
Args: {
alignment?: Alignment;
controlElement?: HTMLElement;
isShown?: boolean;
flip?: boolean;
offset?: string | number;
side?: Direction;

Expand Down Expand Up @@ -80,6 +80,7 @@ const Tooltip: TOC<TooltipSignature> = <template>
<Popover
class="tooltip"
@alignment={{@alignment}}
@flip={{@flip}}
@offset={{@offset}}
@side={{@side}}
@onShow={{@onShow}}
Expand Down
Loading