Skip to content

Commit

Permalink
fix(popover): using min-width for fit-size (#510)
Browse files Browse the repository at this point in the history
Co-authored-by: Murat Çorlu <[email protected]>
  • Loading branch information
ogunb and muratcorlu authored Apr 5, 2023
1 parent 0179f78 commit 08615a4
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 97 deletions.
40 changes: 6 additions & 34 deletions src/components/dropdown/bl-dropdown.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,42 +3,14 @@
display: inline-block;
}

.popover {
--left: 0;
--top: 0;
--border-color: var(--bl-color-primary);

position: fixed;
z-index: var(--bl-index-popover);
display: none;
flex-direction: column;
align-items: flex-start;
padding: var(--bl-size-m);
gap: var(--bl-size-xs);
overflow-y: auto;
background: var(--bl-color-primary-background);
border: 1px solid var(--border-color);

/* FIXME: Use variables */
box-shadow: 0 10px 15px -8px #27314226;
border-radius: var(--bl-size-3xs);
left: var(--left);
top: var(--top);
box-sizing: border-box;
}

:host([kind='neutral']) .popover {
--border-color: var(--bl-color-secondary);
}

:host([kind='success']) .popover {
--border-color: var(--bl-color-success);
:host([kind='neutral']) bl-popover {
--bl-popover-border-color: var(--bl-color-secondary);
}

:host([kind='danger']) .popover {
--border-color: var(--bl-color-danger);
:host([kind='success']) bl-popover {
--bl-popover-border-color: var(--bl-color-success);
}

.visible {
display: flex;
:host([kind='danger']) bl-popover {
--bl-popover-border-color: var(--bl-color-danger);
}
26 changes: 25 additions & 1 deletion src/components/dropdown/bl-dropdown.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import { sendKeys } from '@web/test-runner-commands';

import type typeOfBlDropdown from './bl-dropdown';
import BlButton from '../button/bl-button';
import '../popover/bl-popover';
import BlPopover from '../popover/bl-popover';

describe('bl-dropdown', () => {
it('is defined', () => {
Expand All @@ -33,7 +35,7 @@ describe('bl-dropdown', () => {
>
Dropdown Button
</bl-button>
<div class="popover" aria-expanded="false" role="menu"><slot></slot></div>
<bl-popover placement="bottom-start" fit-size><slot></slot></bl-popover>
`
);
});
Expand All @@ -43,23 +45,28 @@ describe('bl-dropdown', () => {

const buttonHost = <BlButton>el.shadowRoot?.querySelector('bl-button');
const button = buttonHost.shadowRoot?.querySelector('.button') as HTMLElement | null;
const popover = <BlPopover>el.shadowRoot?.querySelector('bl-popover');

button?.click();

expect(el.opened).to.true;
expect(popover.visible).to.true;
});

it('should close dropdown', async () => {
const el = await fixture<typeOfBlDropdown>(html`<bl-dropdown></bl-dropdown>`);

const buttonHost = <BlButton>el.shadowRoot?.querySelector('bl-button');
const button = buttonHost.shadowRoot?.querySelector('.button') as HTMLElement | null;
const popover = <BlPopover>el.shadowRoot?.querySelector('bl-popover');

button?.click();
expect(el.opened).to.true;
expect(popover.visible).to.true;

button?.click();
expect(el.opened).to.false;
expect(popover.visible).to.false;
});

it('should close dropdown when click outside', async () => {
Expand All @@ -69,15 +76,18 @@ describe('bl-dropdown', () => {

const buttonHost = <BlButton>el.shadowRoot?.querySelector('bl-button');
const button = buttonHost.shadowRoot?.querySelector('.button') as HTMLElement | null;
const popover = <BlPopover>el.shadowRoot?.querySelector('bl-popover');

button?.click();
expect(el.opened).to.true;
expect(popover.visible).to.true;

const body = <HTMLBodyElement>el.closest('body');
body.click();

setTimeout(() => {
expect(el.opened).to.false;
expect(popover.visible).to.false;
});
});

Expand Down Expand Up @@ -113,6 +123,20 @@ describe('bl-dropdown', () => {
expect(event.detail).to.be.equal('Dropdown closed!');
});

it('should not change opened property when disabled', async () => {
const el = await fixture<typeOfBlDropdown>(html`<bl-dropdown disabled></bl-dropdown>`);
expect(el.opened).to.false;

const buttonHost = <BlButton>el.shadowRoot?.querySelector('bl-button');
const button = buttonHost.shadowRoot?.querySelector('.button') as HTMLElement | null;
const popover = <BlPopover>el.shadowRoot?.querySelector('bl-popover');

button?.click();

expect(el.opened).to.false;
expect(popover.visible).to.false;
});

describe('keyboard navigation', () => {
it('should focus next action with down arrow key', async () => {
//when
Expand Down
82 changes: 21 additions & 61 deletions src/components/dropdown/bl-dropdown.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,15 @@
import { LitElement, html, CSSResultGroup, TemplateResult } from 'lit';
import { customElement, property, state, query } from 'lit/decorators.js';
import {
computePosition,
flip,
offset,
autoUpdate,
size,
MiddlewareArguments,
} from '@floating-ui/dom';
import { event, EventDispatcher } from '../../utilities/event';
import { classMap } from 'lit/directives/class-map.js';

import style from './bl-dropdown.css';

import '../button/bl-button';
import { ButtonSize, ButtonVariant, ButtonKind } from '../button/bl-button';
import BlButton, { ButtonSize, ButtonVariant, ButtonKind } from '../button/bl-button';
import { ifDefined } from 'lit/directives/if-defined.js';

import BlDropdownItem, { blDropdownItemTag } from './item/bl-dropdown-item';

export type CleanUpFunction = () => void;
import BlPopover from '../popover/bl-popover';

export const blDropdownTag = 'bl-dropdown';

Expand All @@ -33,13 +23,11 @@ export default class BlDropdown extends LitElement {
return [style];
}

@query('bl-button')
private _dropdownButton: HTMLElement;

@query('.popover')
private _popover: HTMLElement;
@query('bl-popover')
private _popover: BlPopover;

private _cleanUpPopover: CleanUpFunction | null = null;
@query('bl-button')
private _button: BlButton;

@state() private _isPopoverOpen = false;

Expand Down Expand Up @@ -87,13 +75,18 @@ export default class BlDropdown extends LitElement {
super.connectedCallback();
this.addEventListener('keydown', this.handleKeyDown);
}

disconnectedCallback() {
super.disconnectedCallback();

this._cleanUpPopover && this._cleanUpPopover();
this.removeEventListener('keydown', this.handleKeyDown);
}

firstUpdated() {
// `_button` will be undefined during the initial render.
// To ensure proper rendering, we set `_popover.target` after the template has been created.
this._popover.target = this._button;
}

get opened() {
return this._isPopoverOpen;
}
Expand All @@ -102,36 +95,6 @@ export default class BlDropdown extends LitElement {
!this._isPopoverOpen && !this.disabled ? this.open() : this.close();
}

private _handleClickOutside = (event: MouseEvent) => {
const eventPath = event.composedPath() as HTMLElement[];
if (!eventPath.includes(this._popover) && !eventPath.includes(this._dropdownButton)) {
this.close();
}
};

private _setupPopover() {
this._cleanUpPopover = autoUpdate(this._dropdownButton, this._popover, () => {
computePosition(this._dropdownButton, this._popover, {
placement: 'bottom-start',
strategy: 'fixed',
middleware: [
flip(),
offset(8),
size({
apply(args: MiddlewareArguments) {
Object.assign(args.elements.floating.style, {
minWidth: `${args.elements.reference.getBoundingClientRect().width}px`,
});
},
}),
],
}).then(({ x, y }) => {
this._popover.style.setProperty('--left', `${x}px`);
this._popover.style.setProperty('--top', `${y}px`);
});
});
}

private focusedOptionIndex = -1;

private handleKeyDown(event: KeyboardEvent) {
Expand Down Expand Up @@ -169,24 +132,17 @@ export default class BlDropdown extends LitElement {

open() {
this._isPopoverOpen = true;
this._setupPopover();
this._popover.show();
this.onOpen('Dropdown opened!');
document.addEventListener('click', this._handleClickOutside);
}

close() {
this._isPopoverOpen = false;
this._popover.visible && this._popover.hide();
this.onClose('Dropdown closed!');
this._cleanUpPopover && this._cleanUpPopover();
document.removeEventListener('click', this._handleClickOutside);
}

render(): TemplateResult {
const popoverClasses = classMap({
popover: true,
visible: this.opened,
});

return html`<bl-button
dropdown
.active=${this.opened}
Expand All @@ -199,9 +155,13 @@ export default class BlDropdown extends LitElement {
>
${this.label}
</bl-button>
<div class="${popoverClasses}" role="menu" aria-expanded="${this.opened}">
<bl-popover
fit-size
placement="bottom-start"
@bl-popover-hide="${this.close}"
>
<slot></slot>
</div> `;
</bl-popover> `;
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/components/popover/bl-popover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ export default class BlPopover extends LitElement {
apply(args: MiddlewareState) {
if (args.elements.floating && args.elements.reference) {
Object.assign(args.elements.floating.style, {
width: `${args.elements.reference.getBoundingClientRect().width}px`,
'min-width': `${args.elements.reference.getBoundingClientRect().width}px`,
});
}
},
Expand Down

0 comments on commit 08615a4

Please sign in to comment.