diff --git a/.circleci/config.yml b/.circleci/config.yml index 46327585807..c0c1a61158d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -8,7 +8,7 @@ executors: parameters: current_golden_images_hash: type: string - default: 0c9792a95631398313aff0fbcc0eedeeabdfb26d + default: 02cacf9ef4e766ce33b46b7075da37f0a3f11052 commands: downstream: steps: diff --git a/packages/color-area/src/spectrum-color-area.css b/packages/color-area/src/spectrum-color-area.css index 277ebca99b5..3ad7c816dc5 100644 --- a/packages/color-area/src/spectrum-color-area.css +++ b/packages/color-area/src/spectrum-color-area.css @@ -108,6 +108,11 @@ THIS FILE IS MACHINE GENERATED. DO NOT EDIT */ ) var(--spectrum-colorarea-border-color); } +.gradient { + /* .spectrum-ColorArea-gradient, + * .spectrum-ColorHandle-color */ + forced-color-adjust: none; +} :host([disabled]) { /* .spectrum-ColorArea.is-disabled */ background: var( @@ -131,3 +136,11 @@ THIS FILE IS MACHINE GENERATED. DO NOT EDIT */ /* .spectrum-ColorArea.is-disabled .spectrum-ColorArea-gradient */ display: none; } +@media (forced-colors: active) { + :host { + --spectrum-colorarea-fill-color-disabled: GrayText; + } + :host([disabled]) { + forced-color-adjust: none; + } +} diff --git a/packages/overlay/src/OverlayTrigger.ts b/packages/overlay/src/OverlayTrigger.ts index 817ed46addb..273e36dc710 100644 --- a/packages/overlay/src/OverlayTrigger.ts +++ b/packages/overlay/src/OverlayTrigger.ts @@ -28,6 +28,8 @@ import { import { openOverlay } from './loader.js'; import overlayTriggerStyles from './overlay-trigger.css.js'; +export type OverlayContentTypes = 'click' | 'hover' | 'longpress'; + /** * A overlay trigger component for displaying overlays relative to other content. * @element overlay-trigger @@ -42,6 +44,7 @@ import overlayTriggerStyles from './overlay-trigger.css.js'; */ export class OverlayTrigger extends LitElement { private closeClickOverlay?: () => void; + private closeLongpressOverlay?: () => void; private closeHoverOverlay?: () => void; public static get styles(): CSSResultArray { @@ -61,6 +64,9 @@ export class OverlayTrigger extends LitElement { @property({ type: Number, reflect: true }) public offset = 6; + @property({ reflect: true }) + public open?: OverlayContentTypes; + @property({ type: Boolean, reflect: true }) public disabled = false; @@ -69,8 +75,8 @@ export class OverlayTrigger extends LitElement { private hoverContent?: HTMLElement; private targetContent?: HTMLElement; - public overloadTarget(target: HTMLElement): void { - this.targetContent = target; + private handleClose(): void { + this.removeAttribute('open'); } protected render(): TemplateResult { @@ -85,6 +91,7 @@ export class OverlayTrigger extends LitElement { @mouseleave=${this.onTrigger} @focusin=${this.onTrigger} @focusout=${this.onTrigger} + @sp-closed=${this.handleClose} > { - if (this.targetContent && this.clickContent) { - if (this.type === 'modal') { - const firstFocusable = this.querySelector( - 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"]), [focusable]' - ) as HTMLElement; - if (!firstFocusable) { - this.clickContent.tabIndex = 0; - } - } - const { targetContent, clickContent } = this; - this.closeClickOverlay = await OverlayTrigger.openOverlay( - targetContent, - this.type ? this.type : 'click', - clickContent, - this.overlayOptions - ); + if (!this.targetContent || !this.clickContent) { + return; } + const { targetContent, clickContent } = this; + this.prepareToFocusOverlayContent(clickContent); + this.closeClickOverlay = await OverlayTrigger.openOverlay( + targetContent, + this.type ? this.type : 'click', + clickContent, + this.overlayOptions + ); } private async onTriggerLongpress( - event: CustomEvent + event?: CustomEvent ): Promise { if (!this.targetContent || !this.longpressContent) { return; } const { targetContent, longpressContent } = this; - if (this.type === 'modal') { - const firstFocusable = this.querySelector( - 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"]), [focusable]' - ) as HTMLElement; - if (!firstFocusable) { - longpressContent.tabIndex = 0; - } - } - const type = event.detail.source === 'keyboard' ? 'click' : 'longpress'; + this.prepareToFocusOverlayContent(longpressContent); + const type = + event && event.detail.source === 'keyboard' ? 'click' : 'longpress'; const interaction = this.type ? this.type : type || 'longpress'; - this.closeClickOverlay = await OverlayTrigger.openOverlay( + this.closeLongpressOverlay = await OverlayTrigger.openOverlay( targetContent, interaction, longpressContent, @@ -211,22 +270,23 @@ export class OverlayTrigger extends LitElement { private hoverOverlayReady = Promise.resolve(); public async onTriggerMouseEnter(): Promise { - if (this.targetContent && this.hoverContent) { - let overlayReady: () => void = () => { - return; - }; - this.hoverOverlayReady = new Promise((res) => { - overlayReady = res; - }); - const { targetContent, hoverContent } = this; - this.closeHoverOverlay = await OverlayTrigger.openOverlay( - targetContent, - 'hover', - hoverContent, - this.overlayOptions - ); - overlayReady(); + if (!this.targetContent || !this.hoverContent) { + return; } + let overlayReady: () => void = () => { + return; + }; + this.hoverOverlayReady = new Promise((res) => { + overlayReady = res; + }); + const { targetContent, hoverContent } = this; + this.closeHoverOverlay = await OverlayTrigger.openOverlay( + targetContent, + 'hover', + hoverContent, + this.overlayOptions + ); + overlayReady(); } public async onTriggerMouseLeave(): Promise { @@ -240,29 +300,28 @@ export class OverlayTrigger extends LitElement { private onClickSlotChange( event: Event & { target: HTMLSlotElement } ): void { - const content = this.extractSlotContentFromEvent(event); - this.clickContent = content; + this.clickContent = this.extractSlotContentFromEvent(event); + this.manageOpen(); } private onLongpressSlotChange( event: Event & { target: HTMLSlotElement } ): void { - const content = this.extractSlotContentFromEvent(event); - this.longpressContent = content; + this.longpressContent = this.extractSlotContentFromEvent(event); + this.manageOpen(); } private onHoverSlotChange( event: Event & { target: HTMLSlotElement } ): void { - const content = this.extractSlotContentFromEvent(event); - this.hoverContent = content; + this.hoverContent = this.extractSlotContentFromEvent(event); + this.manageOpen(); } private onTargetSlotChange( event: Event & { target: HTMLSlotElement } ): void { - const content = this.extractSlotContentFromEvent(event); - this.targetContent = content; + this.targetContent = this.extractSlotContentFromEvent(event); } private extractSlotContentFromEvent(event: Event): HTMLElement | undefined { @@ -276,6 +335,10 @@ export class OverlayTrigger extends LitElement { this.closeClickOverlay(); delete this.closeClickOverlay; } + if (this.closeHoverOverlay) { + this.closeHoverOverlay(); + delete this.closeClickOverlay; + } super.disconnectedCallback(); } } diff --git a/packages/overlay/stories/overlay.stories.ts b/packages/overlay/stories/overlay.stories.ts index 9d32c013ac2..7c3a05d9c80 100644 --- a/packages/overlay/stories/overlay.stories.ts +++ b/packages/overlay/stories/overlay.stories.ts @@ -8,9 +8,8 @@ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTA OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -import { html, TemplateResult } from '@spectrum-web-components/base'; - -import { OverlayTrigger, Placement } from '../'; +import { html, TemplateResult, ifDefined } from '@spectrum-web-components/base'; +import { OverlayContentTypes, OverlayTrigger, Placement } from '../'; import '@spectrum-web-components/action-button/sp-action-button.js'; import '@spectrum-web-components/action-group/sp-action-group.js'; import '@spectrum-web-components/button/sp-button.js'; @@ -20,6 +19,9 @@ import '@spectrum-web-components/icons-workflow/icons/sp-icon-magnify.js'; import '@spectrum-web-components/overlay/overlay-trigger.js'; import { Picker } from '@spectrum-web-components/picker'; import '@spectrum-web-components/picker/sp-picker.js'; +import '@spectrum-web-components/menu/sp-menu.js'; +import '@spectrum-web-components/menu/sp-menu-item.js'; +import '@spectrum-web-components/menu/sp-menu-divider.js'; import '@spectrum-web-components/popover/sp-popover.js'; import '@spectrum-web-components/slider/sp-slider.js'; import '@spectrum-web-components/radio/sp-radio.js'; @@ -119,19 +121,20 @@ export default { }, }; -export const Default = ({ - placement, - offset, -}: { +interface Properties { placement: Placement; offset: number; -}): TemplateResult => { + open?: OverlayContentTypes; +} + +const template = ({ placement, offset, open }: Properties): TemplateResult => { return html` ${storyStyles} Show Popover - + Click to open another popover. - + Click to open a popover. `; }; +export const Default = (args: Properties): TemplateResult => template(args); + +export const openHoverContent = (args: Properties): TemplateResult => + template({ + ...args, + open: 'hover', + }); + +export const openClickContent = (args: Properties): TemplateResult => + template({ + ...args, + open: 'click', + }); + +const extraText = html` +

This is some text.

+

This is some text.

+

+ This is a + link + . +

+`; + export const inline = (): TemplateResult => { const closeEvent = new Event('close', { bubbles: true, composed: true }); return html` Open - + { event.target.dispatchEvent(closeEvent); @@ -199,13 +225,7 @@ export const inline = (): TemplateResult => { -

This is some text.

-

This is some text.

-

- This is a - link - . -

+ ${extraText} `; }; @@ -214,7 +234,7 @@ export const replace = (): TemplateResult => { return html` Open - + { event.target.dispatchEvent(closeEvent); @@ -224,13 +244,7 @@ export const replace = (): TemplateResult => { -

This is some text.

-

This is some text.

-

- This is a - link - . -

+ ${extraText} `; }; @@ -270,13 +284,7 @@ export const modal = (): TemplateResult => { Content of the dialog -

This is some text.

-

This is some text.

-

- This is a - link - . -

+ ${extraText} `; }; @@ -385,8 +393,11 @@ export const updated = (): TemplateResult => { - - + + Click to open popover { Click to open another popover. @@ -447,7 +457,7 @@ export const sideHoverDraggable = (): TemplateResult => { - + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus egestas sed enim sed condimentum. Nunc facilisis scelerisque massa sed luctus. Orci varius natoque penatibus @@ -499,7 +509,7 @@ export const longpress = (): TemplateResult => { }; export const complexModal = (): TemplateResult => { - requestAnimationFrame(async () => { + requestAnimationFrame(() => { const overlay = document.querySelector( `overlay-trigger` ) as OverlayTrigger; @@ -512,7 +522,6 @@ export const complexModal = (): TemplateResult => { picker.open = true; }); }); - trigger.click(); }); return html` - + { + it('displays `click` declaratively', async () => { + const openedSpy = spy(); + const closedSpy = spy(); + const el = await fixture( + (() => html` + + openedSpy()} + @sp-closed=${() => closedSpy()} + > + + + + + `)() + ); + await elementUpdated(el); + + await waitUntil( + () => openedSpy.calledOnce, + 'click content projected to overlay', + { timeout: 2000 } + ); + + el.removeAttribute('open'); + await elementUpdated(el); + + await waitUntil(() => closedSpy.calledOnce, 'click content returned', { + timeout: 2000, + }); + }); +}); diff --git a/packages/overlay/test/overlay-trigger-hover.test.ts b/packages/overlay/test/overlay-trigger-hover.test.ts new file mode 100644 index 00000000000..880791c0e86 --- /dev/null +++ b/packages/overlay/test/overlay-trigger-hover.test.ts @@ -0,0 +1,53 @@ +/* +Copyright 2020 Adobe. All rights reserved. +This file is licensed to you under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may obtain a copy +of the License at http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under +the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS +OF ANY KIND, either express or implied. See the License for the specific language +governing permissions and limitations under the License. +*/ +import { fixture, elementUpdated, waitUntil, html } from '@open-wc/testing'; +import '@spectrum-web-components/popover/sp-popover.js'; +import '@spectrum-web-components/action-button/sp-action-button.js'; +import '@spectrum-web-components/icons-workflow/icons/sp-icon-magnify.js'; +import { OverlayTrigger } from '..'; +import '@spectrum-web-components/overlay/overlay-trigger.js'; +import { spy } from 'sinon'; + +describe('Overlay Trigger - Hover', () => { + it('displays `hover` declaratively', async () => { + const openedSpy = spy(); + const closedSpy = spy(); + const el = await fixture( + (() => html` + + openedSpy()} + @sp-closed=${() => closedSpy()} + > + + + + + `)() + ); + await elementUpdated(el); + + await waitUntil( + () => openedSpy.calledOnce, + 'hover content projected to overlay', + { timeout: 2000 } + ); + + el.removeAttribute('open'); + await elementUpdated(el); + + await waitUntil(() => closedSpy.calledOnce, 'hover content returned', { + timeout: 2000, + }); + }); +}); diff --git a/packages/overlay/test/overlay-trigger-longpress.test.ts b/packages/overlay/test/overlay-trigger-longpress.test.ts index fe8d538d474..4455ffcbbfb 100644 --- a/packages/overlay/test/overlay-trigger-longpress.test.ts +++ b/packages/overlay/test/overlay-trigger-longpress.test.ts @@ -18,6 +18,7 @@ import { } from '@open-wc/testing'; import { ActionButton } from '@spectrum-web-components/action-button'; import '@spectrum-web-components/action-button/sp-action-button.js'; +import '@spectrum-web-components/action-group/sp-action-group.js'; import '@spectrum-web-components/icons-workflow/icons/sp-icon-magnify.js'; import { Popover } from '@spectrum-web-components/popover'; import '@spectrum-web-components/popover/sp-popover.js'; @@ -25,6 +26,7 @@ import { OverlayTrigger } from '..'; import '@spectrum-web-components/overlay/overlay-trigger.js'; import { executeServerCommand } from '@web/test-runner-commands'; import { waitForPredicate } from '../../../test/testing-helpers'; +import { spy } from 'sinon'; describe('Overlay Trigger - Longpress', () => { it('displays `longpress` content', async () => { @@ -36,7 +38,7 @@ describe('Overlay Trigger - Longpress', () => { @@ -98,4 +100,39 @@ describe('Overlay Trigger - Longpress', () => { }); await waitUntil(() => !content.open, 'closes for `pointerdown`'); }); + it('displays `longpress` declaratively', async () => { + const openedSpy = spy(); + const closedSpy = spy(); + const el = await fixture( + (() => html` + + openedSpy()} + @sp-closed=${() => closedSpy()} + > + + + + + `)() + ); + await elementUpdated(el); + + await waitUntil( + () => openedSpy.calledOnce, + 'longpress content projected to overlay', + { timeout: 2000 } + ); + + el.removeAttribute('open'); + await elementUpdated(el); + + await waitUntil( + () => closedSpy.calledOnce, + 'longpress content returned', + { timeout: 2000 } + ); + }); }); diff --git a/packages/tooltip/stories/tooltip.stories.ts b/packages/tooltip/stories/tooltip.stories.ts index 2eeb919b51d..e3640820877 100644 --- a/packages/tooltip/stories/tooltip.stories.ts +++ b/packages/tooltip/stories/tooltip.stories.ts @@ -11,15 +11,13 @@ governing permissions and limitations under the License. */ import '../sp-tooltip.js'; import '@spectrum-web-components/icon/sp-icon'; -import { html, TemplateResult } from '@spectrum-web-components/base'; +import { html, ifDefined, TemplateResult } from '@spectrum-web-components/base'; import { AlertIcon, CheckmarkIcon, InfoIcon, } from '@spectrum-web-components/icons-workflow'; import '@spectrum-web-components/button/sp-button.js'; -import { OverlayTrigger } from '@spectrum-web-components/overlay'; -import '@spectrum-web-components/overlay/overlay-trigger.js'; import { Placement } from '@spectrum-web-components/overlay/src/popper'; const iconOptions: { @@ -258,17 +256,7 @@ const overlayStyles = html` `; -const overlaid = (placement: Placement): TemplateResult => { - requestAnimationFrame(async () => { - const overlay = document.querySelector( - `overlay-trigger[placement="${placement}"]` - ) as OverlayTrigger; - await overlay.updateComplete; - const trigger = (overlay.shadowRoot as ShadowRoot).querySelector( - '#trigger' - ) as HTMLDivElement; - trigger.dispatchEvent(new MouseEvent('mouseenter')); - }); +const overlaid = (openPlacement: Placement): TemplateResult => { return html` ${overlayStyles} ${([ @@ -278,7 +266,12 @@ const overlaid = (placement: Placement): TemplateResult => { ['top', 'info'], ] as [Placement, string][]).map(([placement, variant]) => { return html` - + Hover for ${variant ? variant : 'tooltip'} on the ${placement}