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

build(components): update popover polyfill #955

Closed
wants to merge 22 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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
@@ -0,0 +1,7 @@
{
"type": "none",
"comment": "offload popup code to polyfill",
"packageName": "@vonage/vivid",
"email": "[email protected]",
"dependentChangeType": "none"
}
4 changes: 2 additions & 2 deletions libs/components/src/lib/menu/menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export class Menu extends FastMenu {
*/
@attr({
mode: 'boolean',
}) open? = false;
}) open?= false;

/**
* the placement of the menu
Expand All @@ -39,6 +39,6 @@ export class Menu extends FastMenu {
@attr anchor?: string;

popupOpenChanged = () => {
this.open = (this._popup as Popup).open;
this.open = (this._popup as Popup)._open;
};
}
88 changes: 62 additions & 26 deletions libs/components/src/lib/popup/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,36 +9,50 @@ Popup's goal is to provide additional, helpful content. To trigger the Popup, it
</script>
```

## Members
## Properties

### Anchor

Use the `anchor` attribute to reference the ID to element in the popup’s owner document.

The popup is positioned in a fixed manner to its anchor.
Use the `anchor` attribute to reference the ID of an element (in the same popup's owner document) of which the popup should be anchored to.

- Type: `string`
- Default: `''`

```html preview center
<span id="anchor">
⚓️
</span>
<vwc-popup anchor="anchor" open>
<span id="anchor">Anchor</span>
<vwc-popup popover anchor="anchor" defaultopen>
This is a popup.
</vwc-popup>
```

### Open
### Invokers

(taken from browsers' native `popover` support)

[Supported elements](https://whatpr.org/html/8221/popover.html#attr-popover-target-supported-elements) can be used to invoke the popup display state.
Set `popovertoggletarget`, `popovershowtarget` or `popoverhidetarget` on a supported element to reference the ID to popover element for it to be used as a trigger.
Alternatively, use the attributes respective properties `popoverToggleTargetElement`, `popoverShowTargetElement` or `popoverHideTargetElement` to reference to the popover element(this can come in handy when referencing accross different tree scopes).

Use the `open` attribute to indicate whether the popup is open.
Normally, these attributes are set on the popover's anchored element, but they can be set on any other supported element.

```html preview center
<vwc-button popovertoggletarget="popup" label="toggle popup"></vwc-button>
<span id="anchor">Anchor</span>
<vwc-popup popover id="popup" anchor="anchor" defaultopen>
This is a popup.
</vwc-popup>
```

### Default Open

Use the `defaultopen` attribute to indicate whether the popup should be open when the page loads.

- Type: `boolean`
- Default: `false`

```html preview center
<vwc-button id="anchor" appearance='outlined' label='Click me!'></vwc-button>
<vwc-popup id="popup" anchor="anchor" open>
<vwc-popup id="popup" anchor="anchor" defaultopen>
<vwc-layout gutters="small">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
</vwc-layout>
Expand All @@ -58,7 +72,7 @@ Add the `dismissible` attribute to add a close button to the popup.

```html preview center
<vwc-button id="anchor" appearance='outlined' label='Click on me!'></vwc-button>
<vwc-popup id="popup" anchor="anchor" open dismissible>
<vwc-popup id="popup" anchor="anchor" defaultopen dismissible>
<vwc-layout gutters="small">
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
</vwc-layout>
Expand All @@ -69,6 +83,28 @@ Add the `dismissible` attribute to add a close button to the popup.
</script>
```

### Popover

Use the `popover` attribute to add Light dismiss behaviors.

- When `'auto'` the following will dismiss the popup: ~~pressing the ESC key~~ (TBD), ~~keyboard-navigation~~ (TBD), invoking elsewhere in the document, invoking another popup, etc.
- When `'manual'`, popup cannot be light dismissed (it can only be dismissed by an explicit trigger element or by JavaScript), and they don't automatically dismiss previously-shown popups.

- Type: `'auto'` | `'manual'`
- Default: `'auto'`

```html preview center
<vwc-button id="anchor" appearance='outlined' label='Click outside of the popup!' ></vwc-button>
<vwc-popup id="popover" anchor="anchor" defaultopen popover>
<vwc-layout gutters="small">
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
</vwc-layout>
</vwc-popup>
<script>
anchor.addEventListener('click', () => popover.open = !popover.open);
</script>
```

### Arrow

Add the `arrow` attribute to add a small triangle to indicate the trigger element.
Expand All @@ -78,7 +114,7 @@ Add the `arrow` attribute to add a small triangle to indicate the trigger elemen

```html preview center
<vwc-icon id="anchor" name='info-line'></vwc-icon>
<vwc-popup anchor="anchor" open arrow>
<vwc-popup anchor="anchor" defaultopen arrow>
<vwc-layout gutters="small" style="200px">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
</vwc-layout>
Expand All @@ -94,7 +130,7 @@ Add the `alternate` attribute to set the color-scheme to dark or light (dependin

```html preview center
<vwc-icon id="anchor" name='info-line'></vwc-icon>
<vwc-popup anchor="anchor" open alternate>
<vwc-popup anchor="anchor" defaultopen alternate>
<vwc-layout gutters="small" style="200px">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
</vwc-layout>
Expand Down Expand Up @@ -123,40 +159,40 @@ Use the `placement` attribute to set the placement of the popup around the ancho

<div id="anchor" class="square"></div>

<vwc-popup id="popup" anchor="anchor" open placement="right-end">
<vwc-popup id="popup" anchor="anchor" defaultopen placement="right-end">
right-end
</vwc-popup>
<vwc-popup id="popup" anchor="anchor" open placement="right">
<vwc-popup id="popup" anchor="anchor" defaultopen placement="right">
right
</vwc-popup>
<vwc-popup id="popup" anchor="anchor" open placement="right-start">
<vwc-popup id="popup" anchor="anchor" defaultopen placement="right-start">
right-start
</vwc-popup>
<vwc-popup id="popup" anchor="anchor" open placement="left-end">
<vwc-popup id="popup" anchor="anchor" defaultopen placement="left-end">
left-end
</vwc-popup>
<vwc-popup id="popup" anchor="anchor" open placement="left">
<vwc-popup id="popup" anchor="anchor" defaultopen placement="left">
left
</vwc-popup>
<vwc-popup id="popup" anchor="anchor" open placement="left-start">
<vwc-popup id="popup" anchor="anchor" defaultopen placement="left-start">
left-start
</vwc-popup>
<vwc-popup id="popup" anchor="anchor" open placement="top-end">
<vwc-popup id="popup" anchor="anchor" defaultopen placement="top-end">
top-end
</vwc-popup>
<vwc-popup id="popup" anchor="anchor" open placement="top">
<vwc-popup id="popup" anchor="anchor" defaultopen placement="top">
top
</vwc-popup>
<vwc-popup id="popup" anchor="anchor" open placement="top-start">
<vwc-popup id="popup" anchor="anchor" defaultopen placement="top-start">
top-start
</vwc-popup>
<vwc-popup id="popup" anchor="anchor" open placement="bottom-end">
<vwc-popup id="popup" anchor="anchor" defaultopen placement="bottom-end">
bottom-end
</vwc-popup>
<vwc-popup id="popup" anchor="anchor" open placement="bottom">
<vwc-popup id="popup" anchor="anchor" defaultopen placement="bottom">
bottom
</vwc-popup>
<vwc-popup id="popup" anchor="anchor" open placement="bottom-start">
<vwc-popup id="popup" anchor="anchor" defaultopen placement="bottom-start">
bottom-start
</vwc-popup>
```
Expand Down
125 changes: 125 additions & 0 deletions libs/components/src/lib/popup/popup.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,131 @@ describe('vwc-popup', () => {
});
});

describe('handle popover', () => {
it('should hide when call hidePopover', async () => {
await setAnchor();

const element = await fixture('<vwc-popup popover open></vwc-popup>', ADD_TEMPLATE_TO_FIXTURE) as Popup;
await elementUpdated(element);

element.anchor = 'anchor';
await elementUpdated(element);

const openStateBeforeHidePopover = element.open;

element.hidePopover();
await elementUpdated(element);

expect(openStateBeforeHidePopover)
.toEqual(true);
expect(element.open)
.toEqual(false);
});

it('should hide when popover auto and clicked outside of the popup', async () => {
const wrapper = await fixture('<div></div>', ADD_TEMPLATE_TO_FIXTURE) as HTMLDivElement;
await elementUpdated(wrapper);
await setAnchor();
const element = await fixture('<vwc-popup open popover anchor="anchor"></vwc-popup>', ADD_TEMPLATE_TO_FIXTURE) as Popup;
wrapper.appendChild(element);
await elementUpdated(element);

const openStateBeforeLightDismiss = element.open;

wrapper?.click();
await elementUpdated(element);

expect(openStateBeforeLightDismiss)
.toEqual(true);
expect(element.open)
.toEqual(false);
});

it('should not hide when popover manual and clicked outside of the popup', async () => {
const wrapper = await fixture('<div></div>', ADD_TEMPLATE_TO_FIXTURE) as HTMLDivElement;
await elementUpdated(wrapper);
await setAnchor();
const element = await fixture('<vwc-popup open popover="manual" anchor="anchor"></vwc-popup>',
ADD_TEMPLATE_TO_FIXTURE) as Popup;
wrapper.appendChild(element);
await elementUpdated(element);

const openStateBeforeLightDismiss = element.open;

wrapper?.click();
await elementUpdated(element);

expect(openStateBeforeLightDismiss)
.toEqual(true);
expect(element.open)
.toEqual(true);
});

it('should not hide when clicked on the popup', async () => {
const element = await fixture('<vwc-popup popover></vwc-popup>', ADD_TEMPLATE_TO_FIXTURE) as Popup;
await elementUpdated(element);

await setAnchor();
element.anchor = 'anchor';
await elementUpdated(element);

element.open = true;
const openStateBeforeLightDismiss = element.open;

(element as HTMLElement).click();
await elementUpdated(element);

expect(openStateBeforeLightDismiss)
.toEqual(true);
expect(element.open)
.toEqual(true);
});

// TODO: test is failing due to polyfill differences. need to investigate how to test this properly with JSDOM
// it('should hide when clicked on the anchor', async () => {
// const element = await fixture('<vwc-popup popover open></vwc-popup>', ADD_TEMPLATE_TO_FIXTURE) as Popup;
// await elementUpdated(element);

// const anchorEl = await setAnchor();
// element.anchor = 'anchor';
// await elementUpdated(element);

// const openStateBeforeAnchorClicked = element.open;

// anchorEl.click();

// await elementUpdated(element);

// expect(openStateBeforeAnchorClicked)
// .toEqual(true);
// expect(element.open)
// .toEqual(false);
// });

it('should stay closed when clicked outside of the popup', async () => {
const element = await fixture('<vwc-popup popover></vwc-popup>', ADD_TEMPLATE_TO_FIXTURE) as Popup;
await elementUpdated(element);

await setAnchor();
element.anchor = 'anchor';
await elementUpdated(element);

const buttonEl = await fixture('<vwc-button id="button"></vwc-button>', ADD_TEMPLATE_TO_FIXTURE) as Button;
await elementUpdated(buttonEl);

const openStateBeforeLightDismiss = element.open;

const button = document.querySelector('vwc-button#button');
(button as HTMLElement).click();
await elementUpdated(element);

expect(openStateBeforeLightDismiss)
.toEqual(false);
expect(element.open)
.toEqual(false);
});
});

describe('handle keydown', () => {
// it('should hide on escape key', async () => {
// const anchor = await setupPopupToOpenWithAnchor();
Expand Down
41 changes: 14 additions & 27 deletions libs/components/src/lib/popup/popup.template.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
import { html, ref, when } from '@microsoft/fast-element';
import { classNames } from '@microsoft/fast-web-utilities';
import { html } from '@microsoft/fast-element';
import type { ViewTemplate } from '@microsoft/fast-element';
import type { ElementDefinitionContext, FoundationElementDefinition } from '@microsoft/fast-foundation';
import { Elevation } from '../elevation/elevation';
import { Button } from '../button/button';
// import { Button } from '../button/button';
import type { Popup } from './popup';

const getClasses = ({
open, dismissible, alternate
}: Popup) => classNames(
'control',
['open', Boolean(open)],
['dismissible', Boolean(dismissible)],
['alternate', Boolean(alternate)]
);
// const getClasses = ({
// _open: open, dismissible, alternate
// }: Popup) => classNames(
// 'control',
// ['open', Boolean(open)],
// ['dismissible', Boolean(dismissible)],
// ['alternate', Boolean(alternate)]
// );

/**
* The template for the {@link @vonage/vivid#Popup} component.
Expand All @@ -27,21 +26,9 @@ export const popupTemplate: (
definition: FoundationElementDefinition
) => ViewTemplate<Popup> = (context: ElementDefinitionContext) => {
const elevationTag = context.tagFor(Elevation);
const buttonTag = context.tagFor(Button);
// const buttonTag = context.tagFor(Button);

return html`
<${elevationTag}>
<div class="popup-wrapper ${(x) => x.strategy}" ${ref('popupEl')}>
<div class="${getClasses}" aria-hidden="${(x) => x.open ? 'false' : 'true'}"
part="${(x) => x.alternate ? 'vvd-theme-alternate' : ''}">
<div class="popup-content">
<slot></slot>
${when(x => x.dismissible,
html<Popup>`<${buttonTag} size="condensed" @click="${x => (x.open = false)}"
class="dismissible-button" icon="close-small-solid" shape="pill"></${buttonTag}>`)}
</div>
${when(x => x.arrow, html<Popup>`<div class="arrow" ${ref('arrowEl')}></div>`)}
</div>
</div>
</${elevationTag}>`;
return html`<${elevationTag}></${elevationTag}>`;
};


Loading