From 130ce553ddc7a16343ef934629e0ecd51f617267 Mon Sep 17 00:00:00 2001 From: Mike Iden Date: Tue, 29 Aug 2023 08:57:18 -0400 Subject: [PATCH 01/19] Popover: new element contribution (#588) * feat(popover): initial commit of the new popover component * feat(popover): iterating on popover in storybook * feat(popover): allow popover to show and dismiss * feat(popover): add more examples for a popover * feat(popover): clean up component * feat(popover): adding react examples * feat(popover): add changeset * feat(popover): add unit tests * feat(popover): rename unit test names and vars * feat(popover): fix tests initialization, remove unused tests, add aria-label for dialog * feat(popover): simplify template rendering * feat(popover): remove shadow when on background * feat(popver): fix inline style of react storybook and use existing radius var Co-authored-by: Dane Hillard * feat(popover): update inlines styles for web component storybook * feat(popover): update inlines styles for web component storybook * feat(popover): use storybook dark background * feat(popover): update shadow to latest * feat(popover): fix radius definition * feat(popover): add additional scenario to cover popups with dark contents on different backgrounds * feat(popover): fix focus logic for popover and remove old styles * feat(popover): convert placeholder copy to lorem ipsum * feat(popover): accessibility tweaks for pharos popover * feat(popover): update stories to use label --------- Co-authored-by: Dane Hillard Co-authored-by: Jialin He <38861633+jialin-he@users.noreply.github.com> --- .changeset/light-pens-swim.md | 5 + .storybook/initComponents.js | 2 + .../popover/PharosPopover.react.stories.jsx | 137 ++++++ .../components/popover/pharos-popover.scss | 36 ++ .../components/popover/pharos-popover.test.ts | 411 ++++++++++++++++++ .../src/components/popover/pharos-popover.ts | 388 +++++++++++++++++ .../popover/pharos-popover.wc.stories.jsx | 167 +++++++ packages/pharos/src/index.ts | 1 + packages/pharos/src/test/initComponents.ts | 2 + 9 files changed, 1149 insertions(+) create mode 100644 .changeset/light-pens-swim.md create mode 100644 packages/pharos/src/components/popover/PharosPopover.react.stories.jsx create mode 100644 packages/pharos/src/components/popover/pharos-popover.scss create mode 100644 packages/pharos/src/components/popover/pharos-popover.test.ts create mode 100644 packages/pharos/src/components/popover/pharos-popover.ts create mode 100644 packages/pharos/src/components/popover/pharos-popover.wc.stories.jsx diff --git a/.changeset/light-pens-swim.md b/.changeset/light-pens-swim.md new file mode 100644 index 000000000..f31ae0d2e --- /dev/null +++ b/.changeset/light-pens-swim.md @@ -0,0 +1,5 @@ +--- +'@ithaka/pharos': minor +--- + +added popover component diff --git a/.storybook/initComponents.js b/.storybook/initComponents.js index b041b928f..24962b79b 100644 --- a/.storybook/initComponents.js +++ b/.storybook/initComponents.js @@ -22,6 +22,7 @@ import { PharosLoadingSpinner, PharosModal, PharosPagination, + PharosPopover, PharosProgressBar, PharosRadioButton, PharosRadioGroup, @@ -69,6 +70,7 @@ registerComponents('storybook', [ PharosLoadingSpinner, PharosModal, PharosPagination, + PharosPopover, PharosProgressBar, PharosRadioButton, PharosRadioGroup, diff --git a/packages/pharos/src/components/popover/PharosPopover.react.stories.jsx b/packages/pharos/src/components/popover/PharosPopover.react.stories.jsx new file mode 100644 index 000000000..906a64260 --- /dev/null +++ b/packages/pharos/src/components/popover/PharosPopover.react.stories.jsx @@ -0,0 +1,137 @@ +import { PharosPopover } from '../../react-components/popover/pharos-popover'; +import { PharosButton } from '../../react-components/button/pharos-button'; +import { configureDocsPage } from '@config/docsPageConfig'; +import { PharosContext } from '../../utils/PharosContext'; + +export default { + title: 'Components/Popover', + component: PharosPopover, + decorators: [ + (Story) => ( + + + + ), + ], + parameters: { + docs: { + page: configureDocsPage('popover'), + }, + }, +}; + +export const Base = { + render: () => ( +
+ + Click Me + + +
Lorem ipsum dolor sit amet
+
+
+ ), +}; + +export const Events = { + render: () => ( +
+ + Click Me + + +
+
Lorem ipsum dolor sit amet
+ { + const menu = document.querySelector('storybook-pharos-popover'); + menu.open = false; + }} + > + Close + +
+
+
+ ), +}; + +export const DarkPopover = { + render: () => ( +
+ + Click Me + + +
+
Lorem ipsum dolor sit amet
+ { + const menu = document.querySelector('storybook-pharos-popover'); + menu.open = false; + }} + > + Close + +
+
+
+ ), +}; + +export const DarkPopoverOnBackground = { + render: () => ( +
+ + Click Me + + +
+
Lorem ipsum dolor sit amet
+ { + const menu = document.querySelector('storybook-pharos-popover'); + menu.open = false; + }} + > + Close + +
+
+
+ ), + parameters: { + backgrounds: { default: 'dark' }, + }, +}; + +export const LargeContents = { + render: () => ( +
+ + Click Me + + +
+

Large Pharos Popover

+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor + incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud + exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure + dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. + Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt + mollit anim id est laborum. +
+ { + const menu = document.querySelector('storybook-pharos-popover'); + menu.open = false; + }} + > + Close + +
+
+
+ ), +}; diff --git a/packages/pharos/src/components/popover/pharos-popover.scss b/packages/pharos/src/components/popover/pharos-popover.scss new file mode 100644 index 000000000..7b991dfad --- /dev/null +++ b/packages/pharos/src/components/popover/pharos-popover.scss @@ -0,0 +1,36 @@ +@use '../../utils/scss/mixins'; + +:host { + display: block; + position: fixed; + z-index: 140; + pointer-events: none; + top: 0; + left: 0; +} + +.popover { + @include mixins.interactive-focus; + + position: relative; + margin: 0; + box-shadow: 0 1px 2px rgb(18 18 18 / 0.3), 0 4px 8px 3px rgb(18 18 18 / 0.15); + border-radius: var(--pharos-radius-base-standard); + visibility: hidden; + pointer-events: none; + opacity: 0; + transform: translate3d(0, 0, 0); + transition: visibility 0s var(--pharos-transition-duration-short), + opacity var(--pharos-transition-duration-short) linear; +} + +:host([open]) .popover { + visibility: visible; + opacity: 1; + transition-delay: 0ms; + pointer-events: auto; +} + +:host([is-on-background]) .popover { + box-shadow: none; +} diff --git a/packages/pharos/src/components/popover/pharos-popover.test.ts b/packages/pharos/src/components/popover/pharos-popover.test.ts new file mode 100644 index 000000000..a92bb29aa --- /dev/null +++ b/packages/pharos/src/components/popover/pharos-popover.test.ts @@ -0,0 +1,411 @@ +import { fixture, expect, aTimeout, elementUpdated } from '@open-wc/testing'; +import { html } from 'lit/static-html.js'; +import sinon from 'sinon'; +import type { SinonSpy } from 'sinon'; +import type { PharosPopover } from './pharos-popover'; + +describe('pharos-popover', () => { + let component: PharosPopover, logSpy: SinonSpy; + + beforeEach(async () => { + component = await fixture(html` + +
I am popover contents
+
+ `); + }); + + before(() => { + logSpy = sinon.spy(console, 'error'); + }); + + after(() => { + logSpy.restore(); + }); + + const getSimplePopover = () => { + return html` + +
I am popover contents
+
+ `; + }; + + it('is accessible', async () => { + await expect(component).to.be.accessible(); + }); + + it('is accessible when open', async () => { + component.open = true; + await component.updateComplete; + + await expect(component).to.be.accessible(); + }); + + it('sets aria attributes on the trigger element', async () => { + const trigger = document.createElement('button'); + trigger.setAttribute('id', 'trigger'); + trigger.setAttribute('data-popover-id', 'my-popover'); + document.body.appendChild(trigger); + + component = await fixture(getSimplePopover()); + + trigger.click(); + await component.updateComplete; + expect(trigger.getAttribute('aria-expanded')).to.equal('true'); + expect(trigger.getAttribute('aria-haspopup')).to.equal('true'); + expect(trigger.getAttribute('aria-controls')).to.equal(component.getAttribute('id')); + }); + + it('opens when the element with matching attribute data-popover-id is clicked', async () => { + const trigger = document.createElement('button'); + trigger.setAttribute('id', 'trigger'); + trigger.setAttribute('data-popover-id', 'my-popover'); + document.body.appendChild(trigger); + + component = await fixture(getSimplePopover()); + + trigger.click(); + await component.updateComplete; + expect(component.open).to.be.true; + }); + + it('can support multiple triggers when open and another trigger is clicked', async () => { + const trigger = document.createElement('button'); + trigger.setAttribute('id', 'trigger'); + trigger.setAttribute('data-popover-id', 'my-popover'); + document.body.appendChild(trigger); + + const secondTrigger = document.createElement('button'); + secondTrigger.setAttribute('id', 'trigger2'); + secondTrigger.setAttribute('data-popover-id', 'my-popover'); + document.body.appendChild(secondTrigger); + + component = await fixture(getSimplePopover()); + + trigger.click(); + await component.updateComplete; + + secondTrigger.click(); + await component.updateComplete; + await aTimeout(150); + expect(component.open).to.be.true; + expect(component['_currentTrigger'] === secondTrigger).to.be.true; + }); + + it('opens when the element with matching attribute data-popover-id and attribute data-popover-hover is hovered', async () => { + const trigger = document.createElement('button'); + trigger.setAttribute('id', 'trigger'); + trigger.setAttribute('data-popover-id', 'my-popover'); + trigger.setAttribute('data-popover-hover', ''); + document.body.appendChild(trigger); + + component = await fixture(getSimplePopover()); + + trigger.dispatchEvent(new MouseEvent('mouseenter')); + await aTimeout(150); + await component.updateComplete; + expect(component.open).to.be.true; + }); + + it('opens when the element with matching attribute data-popover-id and attribute data-popover-hover is hovered', async () => { + const trigger = document.createElement('button'); + trigger.setAttribute('id', 'trigger'); + trigger.setAttribute('data-popover-id', 'my-popover'); + trigger.setAttribute('data-popover-hover', ''); + document.body.appendChild(trigger); + + component = await fixture(getSimplePopover()); + + trigger.dispatchEvent(new MouseEvent('mouseenter')); + await aTimeout(150); + await component.updateComplete; + expect(component.open).to.be.true; + }); + + it('can support multiple triggers when open and another trigger is hovered', async () => { + const trigger = document.createElement('button'); + trigger.setAttribute('id', 'trigger'); + trigger.setAttribute('data-popover-id', 'my-popover'); + document.body.appendChild(trigger); + + const secondTrigger = document.createElement('button'); + secondTrigger.setAttribute('id', 'trigger2'); + secondTrigger.setAttribute('data-popover-id', 'my-popover'); + secondTrigger.setAttribute('data-popover-hover', ''); + document.body.appendChild(secondTrigger); + + component = await fixture(getSimplePopover()); + + trigger.click(); + await component.updateComplete; + + secondTrigger.dispatchEvent(new MouseEvent('mouseenter')); + await component.updateComplete; + await aTimeout(150); + expect(component.open).to.be.true; + expect(component['_currentTrigger'] === secondTrigger).to.be.true; + }); + + it('remains open when hover is moved from the trigger element to the popover', async () => { + const trigger = document.createElement('button'); + trigger.setAttribute('id', 'trigger'); + trigger.setAttribute('data-popover-id', 'my-popover'); + trigger.setAttribute('data-popover-hover', ''); + document.body.appendChild(trigger); + + component = await fixture(getSimplePopover()); + + trigger.dispatchEvent(new MouseEvent('mouseenter')); + await aTimeout(150); + await component.updateComplete; + + component.dispatchEvent(new MouseEvent('mouseenter')); + await aTimeout(150); + await component.updateComplete; + expect(component.open).to.be.true; + }); + + it('opens when enter key is pressed on the element with attribute data-popover-hover', async () => { + const trigger = document.createElement('button'); + trigger.setAttribute('id', 'trigger'); + trigger.setAttribute('data-popover-id', 'my-popover'); + trigger.setAttribute('data-popover-hover', ''); + document.body.appendChild(trigger); + + component = await fixture(getSimplePopover()); + + trigger.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' })); + await component.updateComplete; + expect(component.open).to.be.true; + }); + + it('opens when space key is pressed on the element with attribute data-popover-hover', async () => { + const trigger = document.createElement('button'); + trigger.setAttribute('id', 'trigger'); + trigger.setAttribute('data-popover-id', 'my-popover'); + trigger.setAttribute('data-popover-hover', ''); + document.body.appendChild(trigger); + + component = await fixture(getSimplePopover()); + + trigger.dispatchEvent(new KeyboardEvent('keydown', { key: ' ' })); + await component.updateComplete; + expect(component.open).to.be.true; + }); + + it('remains open when an element inside is clicked after the popover opens', async () => { + const trigger = document.createElement('button'); + trigger.setAttribute('id', 'trigger'); + trigger.setAttribute('data-popover-id', 'my-popover'); + document.body.appendChild(trigger); + + component = await fixture(getSimplePopover()); + + const button = document.createElement('button'); + component.appendChild(button); + await elementUpdated(component); + + trigger.click(); + await component.updateComplete; + button.click(); + await component.updateComplete; + expect(component.open).to.be.true; + }); + + it('delegates focus back to the element that opened it', async () => { + let activeElement = null; + const onFocusIn = (event: Event): void => { + activeElement = event.composedPath()[0]; + }; + document.addEventListener('focusin', onFocusIn); + + const trigger = document.createElement('button'); + trigger.setAttribute('id', 'trigger'); + trigger.setAttribute('data-popover-id', 'my-popover'); + document.body.appendChild(trigger); + + const button = document.querySelector('#trigger') as HTMLButtonElement; + button.click(); + button.focus(); + await component.updateComplete; + + component.open = false; + await component.updateComplete; + + expect(activeElement === button).to.be.true; + document.removeEventListener('focusin', onFocusIn); + }); + + it('closes when the escape key is pressed', async () => { + component.open = true; + await component.updateComplete; + + document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' })); + await component.updateComplete; + + expect(component.open).to.be.false; + }); + + it('closes when the escape key for IE is pressed', async () => { + component.open = true; + await component.updateComplete; + + document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Esc' })); + await component.updateComplete; + + expect(component.open).to.be.false; + }); + + it('closes when the escape key is pressed in the popover', async () => { + component.open = true; + await component.updateComplete; + + component.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' })); + await component.updateComplete; + + expect(component.open).to.be.false; + }); + + it('closes when the escape key for IE is pressed in the popover', async () => { + component.open = true; + await component.updateComplete; + + component.dispatchEvent(new KeyboardEvent('keydown', { key: 'Esc' })); + await component.updateComplete; + + expect(component.open).to.be.false; + }); + + it('closes when the element with matching attribute data-popover-id is clicked after the popover opens', async () => { + const trigger = document.createElement('button'); + trigger.setAttribute('id', 'trigger'); + trigger.setAttribute('data-popover-id', 'my-popover'); + document.body.appendChild(trigger); + + component = await fixture(getSimplePopover()); + + trigger.click(); + await component.updateComplete; + trigger.click(); + await component.updateComplete; + expect(component.open).to.be.false; + }); + + it('closes when an another outside element is clicked after the popover opens', async () => { + const trigger = document.createElement('button'); + trigger.setAttribute('id', 'trigger'); + trigger.setAttribute('data-popover-id', 'my-popover'); + document.body.appendChild(trigger); + + component = await fixture(getSimplePopover()); + + trigger.click(); + await component.updateComplete; + document.body.click(); + await component.updateComplete; + expect(component.open).to.be.false; + }); + + it('closes when hover is moved away from the popover', async () => { + const trigger = document.createElement('button'); + trigger.setAttribute('id', 'trigger'); + trigger.setAttribute('data-popover-id', 'my-popover'); + trigger.setAttribute('data-popover-hover', ''); + document.body.appendChild(trigger); + + component = await fixture(getSimplePopover()); + + trigger.dispatchEvent(new MouseEvent('mouseenter')); + await aTimeout(150); + await component.updateComplete; + + component.dispatchEvent(new MouseEvent('mouseenter')); + await aTimeout(150); + await component.updateComplete; + + component.dispatchEvent(new MouseEvent('mouseleave')); + await aTimeout(150); + await component.updateComplete; + expect(component.open).to.be.false; + }); + + it('closes when the element with matching attribute data-popover-id and attribute data-popover-hover loses hover', async () => { + const trigger = document.createElement('button'); + trigger.setAttribute('id', 'trigger'); + trigger.setAttribute('data-popover-id', 'my-popover'); + trigger.setAttribute('data-popover-hover', ''); + document.body.appendChild(trigger); + + component = await fixture(getSimplePopover()); + + trigger.dispatchEvent(new MouseEvent('mouseenter')); + await aTimeout(150); + await component.updateComplete; + + trigger.dispatchEvent(new MouseEvent('mouseleave')); + await aTimeout(150); + await component.updateComplete; + expect(component.open).to.be.false; + }); + + it('remains open when the element with attribute data-popover-hover is hovered and then clicked', async () => { + const trigger = document.createElement('button'); + trigger.setAttribute('id', 'trigger'); + trigger.setAttribute('data-popover-id', 'my-popover'); + trigger.setAttribute('data-popover-hover', ''); + document.body.appendChild(trigger); + + component = await fixture(getSimplePopover()); + + trigger.dispatchEvent(new MouseEvent('mouseenter')); + await aTimeout(150); + await component.updateComplete; + trigger.click(); + await component.updateComplete; + + expect(component.open).to.be.true; + }); + + it('can be opened dynamically', async () => { + const trigger = document.createElement('button'); + document.body.appendChild(trigger); + + component = await fixture(getSimplePopover()); + + await component.openWithTrigger(trigger); + + await component.updateComplete; + expect(component.open).to.be.true; + }); + + it('fires a custom event pharos-popover-opened when opened', async () => { + component = await fixture(getSimplePopover()); + + let wasFired = false; + const handleOpened = (): void => { + wasFired = true; + }; + component.addEventListener('pharos-popover-opened', handleOpened); + component.open = true; + await component.updateComplete; + + expect(wasFired).to.be.true; + }); + + it('fires a custom event pharos-popover-closed when closed', async () => { + component = await fixture(getSimplePopover()); + + let wasFired = false; + const handleClosed = (): void => { + wasFired = true; + }; + component.addEventListener('pharos-popover-closed', handleClosed); + component.open = true; + await component.updateComplete; + + component.open = false; + await component.updateComplete; + expect(wasFired).to.be.true; + }); +}); diff --git a/packages/pharos/src/components/popover/pharos-popover.ts b/packages/pharos/src/components/popover/pharos-popover.ts new file mode 100644 index 000000000..d04894a3c --- /dev/null +++ b/packages/pharos/src/components/popover/pharos-popover.ts @@ -0,0 +1,388 @@ +import { html } from 'lit'; +import type { TemplateResult, CSSResultArray, PropertyValues } from 'lit'; +import { popoverStyles } from './pharos-popover.css'; +import ScopedRegistryMixin from '../../utils/mixins/scoped-registry'; +import FocusMixin from '../../utils/mixins/focus'; +import { OverlayElement } from '../base/overlay-element'; +import { FocusTrap } from '@ithaka/focus-trap'; +import { query, property } from 'lit/decorators.js'; +import { ifDefined } from 'lit/directives/if-defined.js'; +import debounce from '../../utils/debounce'; +import { autoUpdate, computePosition, flip, offset } from '../base/overlay-element'; +import type { Placement, PositioningStrategy } from '../base/overlay-element'; +import focusable from '../../utils/focusable'; +export type { Placement, PositioningStrategy }; + +const FOCUS_ELEMENT = `[data-popover-focus]`; + +/** + * Pharos popover component. + * + * @tag pharos-popover + * @slot - Contains the popover contents. + * + * @fires pharos-popover-opened - Fires when the popover is opened + * @fires pharos-popover-closed - Fires when the popover is closed + * + */ +export class PharosPopover extends ScopedRegistryMixin(FocusMixin(OverlayElement)) { + static elementDefinitions = { + 'focus-trap': FocusTrap, + }; + + /** + * Indicates the menu item is displayed on a dark background. + * @attr on-background + */ + @property({ type: Boolean, reflect: true, attribute: 'is-on-background' }) + public isOnBackground = false; + + /** + * Indicates the aria label to apply to the dialog. + * @attr label + */ + @property({ type: String, reflect: true }) + public label?: string; + + /** + * Indicates the aria label to apply to the dialog. + * @attr label + */ + @property({ type: String, reflect: true, attribute: 'labelled-by' }) + public labelledBy?: string; + + @query('.popover') + private _popover!: HTMLUListElement; + + private _triggers!: HTMLElement[]; + private _currentTrigger: Element | null = null; + private _hasHover = false; + private _enterByKey = false; + private _cleanup?: { (): void } = undefined; + + constructor() { + super(); + this.placement = 'bottom-end'; + this._handleClick = this._handleClick.bind(this); + this._handleKeydown = this._handleKeydown.bind(this); + this._handleTriggerClick = this._handleTriggerClick.bind(this); + this._handleTriggerHover = this._handleTriggerHover.bind(this); + this._handleTriggerKeydown = this._handleTriggerKeydown.bind(this); + } + + public static override get styles(): CSSResultArray { + return [popoverStyles]; + } + + public removeAllTriggers(): void { + this._removeTriggerListeners(); + } + + public async openWithTrigger(trigger: HTMLElement): Promise { + if (this._currentTrigger !== trigger) { + await new Promise((r) => setTimeout(r, 100)); + this.removeAllTriggers(); + this._addTriggerElement(trigger); + this._currentTrigger = trigger; + this.open = true; + } + } + + protected override firstUpdated(): void { + this.addEventListener('keydown', this._handlePopoverKeydown); + this._addTriggerListeners(); + } + + protected override update(changedProperties: PropertyValues): void { + super.update && super.update(changedProperties); + } + + private _emitVisibilityChange() { + const details = { + bubbles: true, + composed: true, + }; + + if (this.open) { + this.dispatchEvent(new CustomEvent('pharos-popover-opened', details)); + } else { + this.dispatchEvent(new CustomEvent('pharos-popover-closed', details)); + } + } + + protected override updated(changedProperties: PropertyValues): void { + if (changedProperties.has('open')) { + if (this.open) { + this._setupPopover(); + } + + if (!this._currentTrigger?.hasAttribute('data-popover-hover') || this._enterByKey) { + if (this.open) { + debounce(() => { + this._focusContents(); + }, 1)(); + } else { + this._returnTriggerFocus(); + } + } + + if (!this.open) { + this._currentTrigger = null; + this._enterByKey = false; + if (this._cleanup) { + this._cleanup(); + } + } + + this._setHoverListeners(); + this._setTriggerAttributes(); + this._emitVisibilityChange(); + } + + super.updated(changedProperties); + } + + override connectedCallback(): void { + super.connectedCallback && super.connectedCallback(); + document.addEventListener('click', this._handleClick); + document.addEventListener('keydown', this._handleKeydown); + + this._addTriggerListeners(); + } + + private _removeTriggerListeners(): void { + this._triggers.forEach((trigger) => { + trigger.removeEventListener('click', this._handleTriggerClick); + trigger.removeEventListener('keydown', this._handleTriggerKeydown); + trigger.removeAttribute('aria-haspopup'); + trigger.removeAttribute('aria-controls'); + + if (trigger.hasAttribute('data-popover-hover')) { + trigger.removeEventListener('mouseenter', this._handleTriggerHover); + trigger.removeEventListener('mouseleave', this._handleTriggerHover); + } + }); + this._triggers = []; + } + + override disconnectedCallback(): void { + document.removeEventListener('click', this._handleClick); + document.removeEventListener('keydown', this._handleKeydown); + this._removeTriggerListeners(); + + super.disconnectedCallback && super.disconnectedCallback(); + } + + private _popoverId(): string { + return this.getAttribute('id') || ''; + } + + private _addTriggerListeners(): void { + this._triggers = Array.prototype.slice.call( + document.querySelectorAll(`[data-popover-id="${this._popoverId()}"]`) + ); + + this._triggers.forEach((trigger) => { + this._setupTriggerElement(trigger); + }); + } + + private _addTriggerElement(trigger: HTMLElement): void { + this._setupTriggerElement(trigger); + this._triggers.push(trigger); + } + + private _setupTriggerElement(trigger: HTMLElement) { + trigger.addEventListener('click', this._handleTriggerClick); + trigger.addEventListener('keydown', this._handleTriggerKeydown); + trigger.setAttribute('aria-haspopup', 'true'); + trigger.setAttribute('aria-controls', this._popoverId()); + + if (trigger.hasAttribute('data-popover-hover')) { + trigger.addEventListener('mouseenter', this._handleTriggerHover); + trigger.addEventListener('mouseleave', this._handleTriggerHover); + } + } + + private _setupPopover(): void { + const placement = this.placement === 'auto' ? 'bottom-start' : this.placement; + if (this._currentTrigger) { + this._cleanup = autoUpdate(this._currentTrigger, this, () => { + if (this._currentTrigger && this._popover) { + computePosition(this._currentTrigger, this._popover, { + placement: placement, + strategy: this.strategy, + middleware: [ + offset(8), + flip({ + fallbackPlacements: this._filteredFallbackPlacements, + }), + ], + }).then(({ x, y }) => { + Object.assign(this._popover.style, { + left: `${x}px`, + top: `${y}px`, + }); + }); + } + }); + } + } + + private async _handleTriggerClick(event: MouseEvent): Promise { + const trigger = event.currentTarget as Element; + const otherTriggerClicked = this._currentTrigger && this._currentTrigger !== trigger; + this._currentTrigger = trigger; + + if (this._hasHover && this.open) { + return; + } else if (!otherTriggerClicked) { + this.open = !this.open; + } else { + this.open = false; + await this.updateComplete; + debounce(() => { + this._currentTrigger = trigger; + this.open = true; + }, 150)(); + } + } + + private _handleTriggerKeydown(event: KeyboardEvent): void { + switch (event.key) { + case 'Enter': + case ' ': + case 'Spacebar': + if ((event.target as Element)?.hasAttribute('data-popover-hover')) { + event.preventDefault(); + this._openPopover(event); + } + break; + } + } + + private async _handleTriggerHover(event: MouseEvent): Promise { + if (event.type === 'mouseenter') { + const otherTriggerClicked = this._currentTrigger && this._currentTrigger !== event.target; + this._currentTrigger = event.target as Element; + this._hasHover = true; + + if (otherTriggerClicked) { + this.open = false; + await this.updateComplete; + this._currentTrigger = event.target as Element; + } + } else if (event.type === 'mouseleave') { + this._hasHover = false; + } + debounce(() => { + this._setOpen(); + }, 150)(); + } + + private _handleKeydown(event: KeyboardEvent): void { + if ((event.key === 'Escape' || event.key === 'Esc') && this.open) { + event.stopPropagation(); + this.open = false; + } + } + + private _handleClick(event: MouseEvent): void { + const targetClicked = this._triggers.find( + (trigger) => trigger === (event.target as Element)?.closest(trigger.tagName) + ); + const popoverClicked = + this === (event.target as Element)?.closest('[data-pharos-component="PharosPopover"]'); + if (!targetClicked && !popoverClicked && this.open) { + event.stopPropagation(); + this.open = false; + } + } + + private _setOpen(): void { + this.open = this._hasHover; + } + + private _handleHover(event: MouseEvent): void { + if (event.type === 'mouseenter') { + this._hasHover = true; + } else if (event.type === 'mouseleave') { + this._hasHover = false; + } + debounce(() => { + this._setOpen(); + }, 150)(); + } + + private async _focusContents(): Promise { + const focusElement = this.querySelector(FOCUS_ELEMENT); + if (focusElement) { + await 0; + (focusElement as HTMLElement).focus(); + } else { + const tabbable = this.querySelector(focusable); + if (tabbable) { + await 0; + (tabbable as HTMLElement).focus(); + } + } + } + + private _returnTriggerFocus(): void { + if (this._currentTrigger && typeof (this._currentTrigger as HTMLElement).focus === 'function') { + (this._currentTrigger as HTMLElement).focus(); + } + } + + private _handlePopoverKeydown(event: KeyboardEvent): void { + switch (event.key) { + case 'Escape': + case 'Esc': + event.preventDefault(); + this._handleKeydown(event); + break; + } + + event.stopPropagation(); + } + + private _setHoverListeners(): void { + if (this._currentTrigger?.hasAttribute('data-popover-hover') && this.open) { + this.addEventListener('mouseenter', this._handleHover); + this.addEventListener('mouseleave', this._handleHover); + } else { + this.removeEventListener('mouseenter', this._handleHover); + this.removeEventListener('mouseleave', this._handleHover); + } + } + + private _setTriggerAttributes(): void { + if (this.open) { + this._currentTrigger?.setAttribute('aria-expanded', 'true'); + } else { + this._triggers?.forEach((trigger) => { + if (trigger !== this._currentTrigger) { + trigger.removeAttribute('aria-expanded'); + } + }); + } + } + + private _openPopover(event: Event): void { + this._enterByKey = true; + this._handleTriggerClick(event as MouseEvent); + } + + protected override render(): TemplateResult { + return html` + + `; + } +} diff --git a/packages/pharos/src/components/popover/pharos-popover.wc.stories.jsx b/packages/pharos/src/components/popover/pharos-popover.wc.stories.jsx new file mode 100644 index 000000000..ade18c2c6 --- /dev/null +++ b/packages/pharos/src/components/popover/pharos-popover.wc.stories.jsx @@ -0,0 +1,167 @@ +import { html } from 'lit'; + +import { configureDocsPage } from '@config/docsPageConfig'; + +export default { + title: 'Components/Popover', + component: 'pharos-popover', + parameters: { + docs: { + page: configureDocsPage('popover'), + }, + options: { selectedPanel: 'addon-controls' }, + }, +}; + +export const Base = { + render: () => + html` +
+ + Click Me + + +
Lorem ipsum dolor sit amet
+
+
+ `, +}; + +export const Events = { + render: () => + html` +
+ + Click Me + + +
+ Lorem ipsum dolor sit amet + + Close + +
+
+
+ `, +}; + +export const DarkPopover = { + render: () => + html` +
+ + Click Me + + +
+ Lorem ipsum dolor sit amet + + Close + +
+
+
+ `, +}; + +export const DarkPopoverOnBackground = { + render: () => + html` +
+ + Click Me + + +
+ Lorem ipsum dolor sit amet + + Close + +
+
+
+ `, + parameters: { + backgrounds: { default: 'dark' }, + }, +}; + +export const LargeContents = { + render: () => + html` +
+ + Click Me + + +
+ Large Pharos Popover +
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor + incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud + exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure + dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. + Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt + mollit anim id est laborum. +
+ + Close + +
+
+
+ `, +}; diff --git a/packages/pharos/src/index.ts b/packages/pharos/src/index.ts index f8dce0dbd..7cb055afe 100644 --- a/packages/pharos/src/index.ts +++ b/packages/pharos/src/index.ts @@ -41,3 +41,4 @@ export { PharosLayout } from './components/layout/pharos-layout'; export { PharosImageCard } from './components/image-card/pharos-image-card'; export { PharosToggleButton } from './components/toggle-button-group/pharos-toggle-button'; export { PharosToggleButtonGroup } from './components/toggle-button-group/pharos-toggle-button-group'; +export { PharosPopover } from './components/popover/pharos-popover'; diff --git a/packages/pharos/src/test/initComponents.ts b/packages/pharos/src/test/initComponents.ts index e6824aa0b..36c664ef7 100644 --- a/packages/pharos/src/test/initComponents.ts +++ b/packages/pharos/src/test/initComponents.ts @@ -22,6 +22,7 @@ import { PharosLoadingSpinner, PharosModal, PharosPagination, + PharosPopover, PharosProgressBar, PharosRadioButton, PharosRadioGroup, @@ -69,6 +70,7 @@ registerComponents('test', [ PharosLoadingSpinner, PharosModal, PharosPagination, + PharosPopover, PharosProgressBar, PharosRadioButton, PharosRadioGroup, From c4d8bc1456e0f24677ac6f49a9d7568adf33c6a2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 29 Aug 2023 09:01:34 -0400 Subject: [PATCH 02/19] chore: version packages (#595) Co-authored-by: github-actions[bot] --- .changeset/light-pens-swim.md | 5 ----- packages/pharos/CHANGELOG.md | 6 ++++++ packages/pharos/package.json | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) delete mode 100644 .changeset/light-pens-swim.md diff --git a/.changeset/light-pens-swim.md b/.changeset/light-pens-swim.md deleted file mode 100644 index f31ae0d2e..000000000 --- a/.changeset/light-pens-swim.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@ithaka/pharos': minor ---- - -added popover component diff --git a/packages/pharos/CHANGELOG.md b/packages/pharos/CHANGELOG.md index 837c23fb5..0dc7f2a3d 100644 --- a/packages/pharos/CHANGELOG.md +++ b/packages/pharos/CHANGELOG.md @@ -1,5 +1,11 @@ # @ithaka/pharos +## 12.24.0 + +### Minor Changes + +- [#588](https://github.com/ithaka/pharos/pull/588) [`130ce55`](https://github.com/ithaka/pharos/commit/130ce553ddc7a16343ef934629e0ecd51f617267) Thanks [@michael-iden](https://github.com/michael-iden)! - added popover component + ## 12.23.0 ### Minor Changes diff --git a/packages/pharos/package.json b/packages/pharos/package.json index 87c81cc84..e304cb3c7 100644 --- a/packages/pharos/package.json +++ b/packages/pharos/package.json @@ -3,7 +3,7 @@ "publishConfig": { "access": "public" }, - "version": "12.23.0", + "version": "12.24.0", "description": "Pharos web components for products and experiences", "files": [ "lib", From 1cb389feec3bf1f273dfe98078e0146887d00a14 Mon Sep 17 00:00:00 2001 From: Christopher Brown Date: Wed, 30 Aug 2023 17:58:27 -0400 Subject: [PATCH 03/19] chore(sidenav): fix flaky test (#600) * chore(sidenav): fix flaky test * chore(sidenav): remove only from test --- packages/pharos/src/components/sidenav/pharos-sidenav.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/pharos/src/components/sidenav/pharos-sidenav.test.ts b/packages/pharos/src/components/sidenav/pharos-sidenav.test.ts index 07bf549ff..8a1c9982e 100644 --- a/packages/pharos/src/components/sidenav/pharos-sidenav.test.ts +++ b/packages/pharos/src/components/sidenav/pharos-sidenav.test.ts @@ -133,6 +133,7 @@ describe('pharos-sidenav', () => { await setViewport({ width: 1055, height: 768 }); component.slide = true; await component.updateComplete; + await aTimeout(1000); await setViewport({ width: 1056, height: 768 }); await component.updateComplete; From 8c223faf6c189e46bc4a8fc9291310947a5d04ba Mon Sep 17 00:00:00 2001 From: Mike Iden Date: Thu, 31 Aug 2023 09:02:21 -0400 Subject: [PATCH 04/19] feat(icon): add compare and side-panel icons (#596) --- .changeset/mighty-teachers-report.md | 5 +++++ packages/pharos/assets/icons/compare.svg | 2 ++ packages/pharos/assets/icons/side-panel.svg | 1 + packages/pharos/tokens/assets/icons.json | 2 ++ 4 files changed, 10 insertions(+) create mode 100644 .changeset/mighty-teachers-report.md create mode 100644 packages/pharos/assets/icons/compare.svg create mode 100644 packages/pharos/assets/icons/side-panel.svg diff --git a/.changeset/mighty-teachers-report.md b/.changeset/mighty-teachers-report.md new file mode 100644 index 000000000..944af532a --- /dev/null +++ b/.changeset/mighty-teachers-report.md @@ -0,0 +1,5 @@ +--- +'@ithaka/pharos': minor +--- + +Add compare and side-panel icons diff --git a/packages/pharos/assets/icons/compare.svg b/packages/pharos/assets/icons/compare.svg new file mode 100644 index 000000000..684f8e070 --- /dev/null +++ b/packages/pharos/assets/icons/compare.svg @@ -0,0 +1,2 @@ + + diff --git a/packages/pharos/assets/icons/side-panel.svg b/packages/pharos/assets/icons/side-panel.svg new file mode 100644 index 000000000..d0c0a76e0 --- /dev/null +++ b/packages/pharos/assets/icons/side-panel.svg @@ -0,0 +1 @@ + diff --git a/packages/pharos/tokens/assets/icons.json b/packages/pharos/tokens/assets/icons.json index 3d3c0bbb6..b93d6fc4e 100644 --- a/packages/pharos/tokens/assets/icons.json +++ b/packages/pharos/tokens/assets/icons.json @@ -28,6 +28,7 @@ "close": { "value": "assets/icons/close.svg" }, "closed-captions-off": { "value": "assets/icons/closed-captions-off.svg" }, "closed-captions-on": { "value": "assets/icons/closed-captions-on.svg" }, + "compare": { "value": "assets/icons/compare.svg" }, "thumbs-up": { "value": "assets/icons/thumbs-up.svg" }, "thumbs-up-filled": { "value": "assets/icons/thumbs-up-filled.svg" }, "thumbs-down": { "value": "assets/icons/thumbs-down.svg" }, @@ -95,6 +96,7 @@ "seek-forward": { "value": "assets/icons/seek-forward.svg" }, "send": { "value": "assets/icons/send.svg" }, "share": { "value": "assets/icons/share.svg" }, + "side-panel": { "value": "assets/icons/side-panel.svg" }, "shift-left": { "value": "assets/icons/shift-left.svg" }, "shift-right": { "value": "assets/icons/shift-right.svg" }, "shift-up": { "value": "assets/icons/shift-up.svg" }, From be65ef56057d889dc229c15ab2c6d4432921edc1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 31 Aug 2023 09:06:45 -0400 Subject: [PATCH 05/19] chore: version packages (#601) Co-authored-by: github-actions[bot] --- .changeset/mighty-teachers-report.md | 5 ----- packages/pharos/CHANGELOG.md | 6 ++++++ packages/pharos/package.json | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) delete mode 100644 .changeset/mighty-teachers-report.md diff --git a/.changeset/mighty-teachers-report.md b/.changeset/mighty-teachers-report.md deleted file mode 100644 index 944af532a..000000000 --- a/.changeset/mighty-teachers-report.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@ithaka/pharos': minor ---- - -Add compare and side-panel icons diff --git a/packages/pharos/CHANGELOG.md b/packages/pharos/CHANGELOG.md index 0dc7f2a3d..8a63d813b 100644 --- a/packages/pharos/CHANGELOG.md +++ b/packages/pharos/CHANGELOG.md @@ -1,5 +1,11 @@ # @ithaka/pharos +## 12.25.0 + +### Minor Changes + +- [#596](https://github.com/ithaka/pharos/pull/596) [`8c223fa`](https://github.com/ithaka/pharos/commit/8c223faf6c189e46bc4a8fc9291310947a5d04ba) Thanks [@michael-iden](https://github.com/michael-iden)! - Add compare and side-panel icons + ## 12.24.0 ### Minor Changes diff --git a/packages/pharos/package.json b/packages/pharos/package.json index e304cb3c7..cb4460e23 100644 --- a/packages/pharos/package.json +++ b/packages/pharos/package.json @@ -3,7 +3,7 @@ "publishConfig": { "access": "public" }, - "version": "12.24.0", + "version": "12.25.0", "description": "Pharos web components for products and experiences", "files": [ "lib", From 834f7ab8976f194bc641db244671338e7f7ab023 Mon Sep 17 00:00:00 2001 From: Jialin He <38861633+jialin-he@users.noreply.github.com> Date: Wed, 6 Sep 2023 15:29:44 -0400 Subject: [PATCH 06/19] Dropdown: add line rules for light variants (#599) * fix(dropdown): add line rules for light variants * docs(dropdown): add changeset * fix(dropdown): fix border radius --- .changeset/eleven-spoons-battle.md | 5 +++++ .../components/dropdown-menu/pharos-dropdown-menu-item.scss | 4 ++++ .../src/components/dropdown-menu/pharos-dropdown-menu.scss | 1 + 3 files changed, 10 insertions(+) create mode 100644 .changeset/eleven-spoons-battle.md diff --git a/.changeset/eleven-spoons-battle.md b/.changeset/eleven-spoons-battle.md new file mode 100644 index 000000000..232c89f13 --- /dev/null +++ b/.changeset/eleven-spoons-battle.md @@ -0,0 +1,5 @@ +--- +'@ithaka/pharos': patch +--- + +Add line rules to light variant of dropdown diff --git a/packages/pharos/src/components/dropdown-menu/pharos-dropdown-menu-item.scss b/packages/pharos/src/components/dropdown-menu/pharos-dropdown-menu-item.scss index 6f7e12b47..ed6d3589e 100644 --- a/packages/pharos/src/components/dropdown-menu/pharos-dropdown-menu-item.scss +++ b/packages/pharos/src/components/dropdown-menu/pharos-dropdown-menu-item.scss @@ -120,6 +120,10 @@ border-radius: 0 0 var(--pharos-radius-base-standard) var(--pharos-radius-base-standard); } +:host(:not([on-background])) .dropdown-menu-item:not(.dropdown-menu-item--last) { + border-bottom: 1px solid var(--pharos-dropdown-menu-item-color-border-base); +} + :host(:not([disabled], [on-background])) .dropdown-menu-item:hover:not(.dropdown-menu-item--active) { .dropdown-menu-item__link, diff --git a/packages/pharos/src/components/dropdown-menu/pharos-dropdown-menu.scss b/packages/pharos/src/components/dropdown-menu/pharos-dropdown-menu.scss index 407bb3531..e80133c44 100644 --- a/packages/pharos/src/components/dropdown-menu/pharos-dropdown-menu.scss +++ b/packages/pharos/src/components/dropdown-menu/pharos-dropdown-menu.scss @@ -14,6 +14,7 @@ background-color: var(--pharos-color-ui-10); position: relative; + contain: paint; margin: 0; list-style-type: none; padding-left: 0; From 6be7f56cbdbe04a90c8d2a42e1847feb41c8ea09 Mon Sep 17 00:00:00 2001 From: Dane Hillard Date: Wed, 6 Sep 2023 16:05:55 -0400 Subject: [PATCH 07/19] chore: bump dependencies and update resolutions (#605) --- package.json | 9 +- packages/pharos/package.json | 2 +- yarn.lock | 488 ++--------------------------------- 3 files changed, 31 insertions(+), 468 deletions(-) diff --git a/package.json b/package.json index 4a589bbe3..ce863367a 100644 --- a/package.json +++ b/package.json @@ -120,7 +120,7 @@ "stylelint-config-prettier": "^9.0.5", "stylelint-config-standard-scss": "^10.0.0", "stylelint-scss": "^5.0.1", - "svglint": "^2.2.0", + "svglint": "^2.4.0", "vite": "^4.3.9" }, "workspaces": { @@ -131,17 +131,16 @@ "resolutions": { "@typescript-eslint/parser": "^5.41.0", "axe-core": "^4.3.3", - "playwright": "^1.17.1", "ansi-regex": "^5.0.1", "nth-check": "^2.0.1", "semver-regex": "^3.1.3", - "immer": "^9.0.6", "object-path": "^0.11.8", "glob-parent": "^6.0.2", "browserslist": "^4.16.5", "trim": "^0.0.3", - "marked": "^0.7.0", - "graphql-config": "^4.1.0" + "loader-utils": "^2.0.3", + "marked": "^4.0.10", + "json5": "^2.2.2" }, "size-limit": [ { diff --git a/packages/pharos/package.json b/packages/pharos/package.json index cb4460e23..32c8125e9 100644 --- a/packages/pharos/package.json +++ b/packages/pharos/package.json @@ -84,7 +84,7 @@ "react-dom": "^17.0.1", "replace-in-file": "^6.3.2", "rimraf": "^3.0.2", - "sassdoc": "^2.7.3", + "sassdoc": "^2.7.4", "sinon": "^12.0.1", "style-dictionary": "^3.0.1", "ts-lit-plugin": "^1.2.1", diff --git a/yarn.lock b/yarn.lock index 338caf0d1..7b089f570 100644 --- a/yarn.lock +++ b/yarn.lock @@ -49,13 +49,6 @@ signedsource "^1.0.0" yargs "^15.3.1" -"@ardatan/sync-fetch@^0.0.1": - version "0.0.1" - resolved "https://registry.yarnpkg.com/@ardatan/sync-fetch/-/sync-fetch-0.0.1.tgz#3385d3feedceb60a896518a1db857ec1e945348f" - integrity sha512-xhlTqH0m31mnsG0tIP4ETgfSB6gXDaYYsUWTrlUV93fFQPI9dd8hE0Ot6MHLCtqgB32hwJAC3YZMWlXZw7AleA== - dependencies: - node-fetch "^2.6.1" - "@aw-web-design/x-default-browser@1.4.126": version "1.4.126" resolved "https://registry.npmjs.org/@aw-web-design/x-default-browser/-/x-default-browser-1.4.126.tgz#43e4bd8f0314ed907a8718d7e862a203af79bc16" @@ -3299,16 +3292,6 @@ parse-filepath "^1.0.2" tslib "~2.4.0" -"@graphql-tools/batch-execute@^8.5.22": - version "8.5.22" - resolved "https://registry.yarnpkg.com/@graphql-tools/batch-execute/-/batch-execute-8.5.22.tgz#a742aa9d138fe794e786d8fb6429665dc7df5455" - integrity sha512-hcV1JaY6NJQFQEwCKrYhpfLK8frSXDbtNMoTur98u10Cmecy1zrqNKSqhEyGetpgHxaJRqszGzKeI3RuroDN6A== - dependencies: - "@graphql-tools/utils" "^9.2.1" - dataloader "^2.2.2" - tslib "^2.4.0" - value-or-promise "^1.0.12" - "@graphql-tools/code-file-loader@^7.2.14": version "7.3.7" resolved "https://registry.yarnpkg.com/@graphql-tools/code-file-loader/-/code-file-loader-7.3.7.tgz#28fd764f9e5d0914b24afbb21d0e3630bcac855a" @@ -3320,79 +3303,6 @@ tslib "^2.4.0" unixify "^1.0.0" -"@graphql-tools/delegate@^9.0.31": - version "9.0.35" - resolved "https://registry.yarnpkg.com/@graphql-tools/delegate/-/delegate-9.0.35.tgz#94683f4bcec63520b4a6c8b2abf2e2e9324ea4f1" - integrity sha512-jwPu8NJbzRRMqi4Vp/5QX1vIUeUPpWmlQpOkXQD2r1X45YsVceyUUBnktCrlJlDB4jPRVy7JQGwmYo3KFiOBMA== - dependencies: - "@graphql-tools/batch-execute" "^8.5.22" - "@graphql-tools/executor" "^0.0.20" - "@graphql-tools/schema" "^9.0.19" - "@graphql-tools/utils" "^9.2.1" - dataloader "^2.2.2" - tslib "^2.5.0" - value-or-promise "^1.0.12" - -"@graphql-tools/executor-graphql-ws@^0.0.14": - version "0.0.14" - resolved "https://registry.yarnpkg.com/@graphql-tools/executor-graphql-ws/-/executor-graphql-ws-0.0.14.tgz#e0f53fc4cfc8a06cc461b2bc1edb4bb9a8e837ed" - integrity sha512-P2nlkAsPZKLIXImFhj0YTtny5NQVGSsKnhi7PzXiaHSXc6KkzqbWZHKvikD4PObanqg+7IO58rKFpGXP7eeO+w== - dependencies: - "@graphql-tools/utils" "^9.2.1" - "@repeaterjs/repeater" "3.0.4" - "@types/ws" "^8.0.0" - graphql-ws "5.12.1" - isomorphic-ws "5.0.0" - tslib "^2.4.0" - ws "8.13.0" - -"@graphql-tools/executor-http@^0.1.7": - version "0.1.10" - resolved "https://registry.yarnpkg.com/@graphql-tools/executor-http/-/executor-http-0.1.10.tgz#faf48e18e62a925796c9653c2f50cf2095bc8e6f" - integrity sha512-hnAfbKv0/lb9s31LhWzawQ5hghBfHS+gYWtqxME6Rl0Aufq9GltiiLBcl7OVVOnkLF0KhwgbYP1mB5VKmgTGpg== - dependencies: - "@graphql-tools/utils" "^9.2.1" - "@repeaterjs/repeater" "^3.0.4" - "@whatwg-node/fetch" "^0.8.1" - dset "^3.1.2" - extract-files "^11.0.0" - meros "^1.2.1" - tslib "^2.4.0" - value-or-promise "^1.0.12" - -"@graphql-tools/executor-legacy-ws@^0.0.11": - version "0.0.11" - resolved "https://registry.yarnpkg.com/@graphql-tools/executor-legacy-ws/-/executor-legacy-ws-0.0.11.tgz#a1e12be8279e92a363a23d4105461a34cd9e389e" - integrity sha512-4ai+NnxlNfvIQ4c70hWFvOZlSUN8lt7yc+ZsrwtNFbFPH/EroIzFMapAxM9zwyv9bH38AdO3TQxZ5zNxgBdvUw== - dependencies: - "@graphql-tools/utils" "^9.2.1" - "@types/ws" "^8.0.0" - isomorphic-ws "5.0.0" - tslib "^2.4.0" - ws "8.13.0" - -"@graphql-tools/executor@^0.0.20": - version "0.0.20" - resolved "https://registry.yarnpkg.com/@graphql-tools/executor/-/executor-0.0.20.tgz#d51d159696e839522dd49d936636af251670e425" - integrity sha512-GdvNc4vszmfeGvUqlcaH1FjBoguvMYzxAfT6tDd4/LgwymepHhinqLNA5otqwVLW+JETcDaK7xGENzFomuE6TA== - dependencies: - "@graphql-tools/utils" "^9.2.1" - "@graphql-typed-document-node/core" "3.2.0" - "@repeaterjs/repeater" "^3.0.4" - tslib "^2.4.0" - value-or-promise "^1.0.12" - -"@graphql-tools/graphql-file-loader@^7.3.7": - version "7.5.17" - resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-file-loader/-/graphql-file-loader-7.5.17.tgz#7c281617ea3ab4db4d42a2bdb49850f2b937f0f9" - integrity sha512-hVwwxPf41zOYgm4gdaZILCYnKB9Zap7Ys9OhY1hbwuAuC4MMNY9GpUjoTU3CQc3zUiPoYStyRtUGkHSJZ3HxBw== - dependencies: - "@graphql-tools/import" "6.7.18" - "@graphql-tools/utils" "^9.2.1" - globby "^11.0.3" - tslib "^2.4.0" - unixify "^1.0.0" - "@graphql-tools/graphql-tag-pluck@7.3.7": version "7.3.7" resolved "https://registry.yarnpkg.com/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-7.3.7.tgz#5f493d5d43c5adb2aa989bc031be6d672ccbdcd6" @@ -3404,25 +3314,6 @@ "@graphql-tools/utils" "8.13.0" tslib "^2.4.0" -"@graphql-tools/import@6.7.18": - version "6.7.18" - resolved "https://registry.yarnpkg.com/@graphql-tools/import/-/import-6.7.18.tgz#ad092d8a4546bb6ffc3e871e499eec7ac368680b" - integrity sha512-XQDdyZTp+FYmT7as3xRWH/x8dx0QZA2WZqfMF5EWb36a0PiH7WwlRQYIdyYXj8YCLpiWkeBXgBRHmMnwEYR8iQ== - dependencies: - "@graphql-tools/utils" "^9.2.1" - resolve-from "5.0.0" - tslib "^2.4.0" - -"@graphql-tools/json-file-loader@^7.3.7": - version "7.4.18" - resolved "https://registry.yarnpkg.com/@graphql-tools/json-file-loader/-/json-file-loader-7.4.18.tgz#d78ae40979bde51cfc59717757354afc9e35fba2" - integrity sha512-AJ1b6Y1wiVgkwsxT5dELXhIVUPs/u3VZ8/0/oOtpcoyO/vAeM5rOvvWegzicOOnQw8G45fgBRMkkRfeuwVt6+w== - dependencies: - "@graphql-tools/utils" "^9.2.1" - globby "^11.0.3" - tslib "^2.4.0" - unixify "^1.0.0" - "@graphql-tools/load@^7.5.10": version "7.8.1" resolved "https://registry.yarnpkg.com/@graphql-tools/load/-/load-7.8.1.tgz#b8fc982e9e1d7b0f2491815cc00955467d2b488c" @@ -3433,16 +3324,6 @@ p-limit "3.1.0" tslib "^2.4.0" -"@graphql-tools/load@^7.5.5": - version "7.8.14" - resolved "https://registry.yarnpkg.com/@graphql-tools/load/-/load-7.8.14.tgz#f2356f9a5f658a42e33934ae036e4b2cadf2d1e9" - integrity sha512-ASQvP+snHMYm+FhIaLxxFgVdRaM0vrN9wW2BKInQpktwWTXVyk+yP5nQUCEGmn0RTdlPKrffBaigxepkEAJPrg== - dependencies: - "@graphql-tools/schema" "^9.0.18" - "@graphql-tools/utils" "^9.2.1" - p-limit "3.1.0" - tslib "^2.4.0" - "@graphql-tools/merge@8.3.7": version "8.3.7" resolved "https://registry.yarnpkg.com/@graphql-tools/merge/-/merge-8.3.7.tgz#05bdb8135012c6d0f298fd0d04ee950daa926078" @@ -3451,14 +3332,6 @@ "@graphql-tools/utils" "8.13.0" tslib "^2.4.0" -"@graphql-tools/merge@^8.2.6", "@graphql-tools/merge@^8.4.1": - version "8.4.2" - resolved "https://registry.yarnpkg.com/@graphql-tools/merge/-/merge-8.4.2.tgz#95778bbe26b635e8d2f60ce9856b388f11fe8288" - integrity sha512-XbrHAaj8yDuINph+sAfuq3QCZ/tKblrTLOpirK0+CAgNlZUCHs0Fa+xtMUURgwCVThLle1AF7svJCxFizygLsw== - dependencies: - "@graphql-tools/utils" "^9.2.1" - tslib "^2.4.0" - "@graphql-tools/optimize@^1.3.0": version "1.3.1" resolved "https://registry.yarnpkg.com/@graphql-tools/optimize/-/optimize-1.3.1.tgz#29407991478dbbedc3e7deb8c44f46acb4e9278b" @@ -3485,35 +3358,6 @@ tslib "^2.4.0" value-or-promise "1.0.11" -"@graphql-tools/schema@^9.0.18", "@graphql-tools/schema@^9.0.19": - version "9.0.19" - resolved "https://registry.yarnpkg.com/@graphql-tools/schema/-/schema-9.0.19.tgz#c4ad373b5e1b8a0cf365163435b7d236ebdd06e7" - integrity sha512-oBRPoNBtCkk0zbUsyP4GaIzCt8C0aCI4ycIRUL67KK5pOHljKLBBtGT+Jr6hkzA74C8Gco8bpZPe7aWFjiaK2w== - dependencies: - "@graphql-tools/merge" "^8.4.1" - "@graphql-tools/utils" "^9.2.1" - tslib "^2.4.0" - value-or-promise "^1.0.12" - -"@graphql-tools/url-loader@^7.9.7": - version "7.17.18" - resolved "https://registry.yarnpkg.com/@graphql-tools/url-loader/-/url-loader-7.17.18.tgz#3e253594d23483e4c0dd3a4c3dd2ad5cd0141192" - integrity sha512-ear0CiyTj04jCVAxi7TvgbnGDIN2HgqzXzwsfcqiVg9cvjT40NcMlZ2P1lZDgqMkZ9oyLTV8Bw6j+SyG6A+xPw== - dependencies: - "@ardatan/sync-fetch" "^0.0.1" - "@graphql-tools/delegate" "^9.0.31" - "@graphql-tools/executor-graphql-ws" "^0.0.14" - "@graphql-tools/executor-http" "^0.1.7" - "@graphql-tools/executor-legacy-ws" "^0.0.11" - "@graphql-tools/utils" "^9.2.1" - "@graphql-tools/wrap" "^9.4.2" - "@types/ws" "^8.0.0" - "@whatwg-node/fetch" "^0.8.0" - isomorphic-ws "^5.0.0" - tslib "^2.4.0" - value-or-promise "^1.0.11" - ws "^8.12.0" - "@graphql-tools/utils@8.13.0", "@graphql-tools/utils@^8.8.0": version "8.13.0" resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-8.13.0.tgz#24aae25d5e684d9d8bcf99a56ba31ac46b4d0275" @@ -3521,30 +3365,6 @@ dependencies: tslib "^2.4.0" -"@graphql-tools/utils@^9.0.0", "@graphql-tools/utils@^9.2.1": - version "9.2.1" - resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-9.2.1.tgz#1b3df0ef166cfa3eae706e3518b17d5922721c57" - integrity sha512-WUw506Ql6xzmOORlriNrD6Ugx+HjVgYxt9KCXD9mHAak+eaXSwuGGPyE60hy9xaDEoXKBsG7SkG69ybitaVl6A== - dependencies: - "@graphql-typed-document-node/core" "^3.1.1" - tslib "^2.4.0" - -"@graphql-tools/wrap@^9.4.2": - version "9.4.2" - resolved "https://registry.yarnpkg.com/@graphql-tools/wrap/-/wrap-9.4.2.tgz#30835587c4c73be1780908a7cb077d8013aa2703" - integrity sha512-DFcd9r51lmcEKn0JW43CWkkI2D6T9XI1juW/Yo86i04v43O9w2/k4/nx2XTJv4Yv+iXwUw7Ok81PGltwGJSDSA== - dependencies: - "@graphql-tools/delegate" "^9.0.31" - "@graphql-tools/schema" "^9.0.18" - "@graphql-tools/utils" "^9.2.1" - tslib "^2.4.0" - value-or-promise "^1.0.12" - -"@graphql-typed-document-node/core@3.2.0", "@graphql-typed-document-node/core@^3.1.1": - version "3.2.0" - resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.2.0.tgz#5f3d96ec6b2354ad6d8a28bf216a1d97b5426861" - integrity sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ== - "@hapi/address@2.x.x": version "2.1.4" resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5" @@ -4563,33 +4383,6 @@ chrome-trace-event "^1.0.2" nullthrows "^1.1.1" -"@peculiar/asn1-schema@^2.3.6": - version "2.3.6" - resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.3.6.tgz#3dd3c2ade7f702a9a94dfb395c192f5fa5d6b922" - integrity sha512-izNRxPoaeJeg/AyH8hER6s+H7p4itk+03QCa4sbxI3lNdseQYCuxzgsuNK8bTXChtLTjpJz6NmXKA73qLa3rCA== - dependencies: - asn1js "^3.0.5" - pvtsutils "^1.3.2" - tslib "^2.4.0" - -"@peculiar/json-schema@^1.1.12": - version "1.1.12" - resolved "https://registry.yarnpkg.com/@peculiar/json-schema/-/json-schema-1.1.12.tgz#fe61e85259e3b5ba5ad566cb62ca75b3d3cd5339" - integrity sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w== - dependencies: - tslib "^2.0.0" - -"@peculiar/webcrypto@^1.4.0": - version "1.4.3" - resolved "https://registry.yarnpkg.com/@peculiar/webcrypto/-/webcrypto-1.4.3.tgz#078b3e8f598e847b78683dc3ba65feb5029b93a7" - integrity sha512-VtaY4spKTdN5LjJ04im/d/joXuvLbQdgy5Z4DXF4MFZhQ+MTrejbNMkfZBp1Bs3O5+bFqnJgyGdPuZQflvIa5A== - dependencies: - "@peculiar/asn1-schema" "^2.3.6" - "@peculiar/json-schema" "^1.1.12" - pvtsutils "^1.3.2" - tslib "^2.5.0" - webcrypto-core "^1.7.7" - "@pkgjs/parseargs@^0.11.0": version "0.11.0" resolved "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" @@ -4852,11 +4645,6 @@ dependencies: "@babel/runtime" "^7.13.10" -"@repeaterjs/repeater@3.0.4", "@repeaterjs/repeater@^3.0.4": - version "3.0.4" - resolved "https://registry.yarnpkg.com/@repeaterjs/repeater/-/repeater-3.0.4.tgz#a04d63f4d1bf5540a41b01a921c9a7fddc3bd1ca" - integrity sha512-AW8PKd6iX3vAZ0vA43nOUOnbq/X5ihgU+mSXXqunMkeQADGiqw/PY0JNeYtD5sr0PAy51YPgAPbDoeapv9r8WA== - "@rollup/plugin-node-resolve@^15.0.1": version "15.0.2" resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.0.2.tgz#8183a80c2cbf7b471f5ac86b16747997f3b5d185" @@ -6745,13 +6533,6 @@ dependencies: "@types/node" "*" -"@types/ws@^8.0.0": - version "8.5.5" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.5.tgz#af587964aa06682702ee6dcbc7be41a80e4b28eb" - integrity sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg== - dependencies: - "@types/node" "*" - "@types/yargs-parser@*": version "21.0.0" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.0.tgz#0c60e537fa790f5f9472ed2776c2b71ec117351b" @@ -7388,33 +7169,6 @@ resolved "https://registry.yarnpkg.com/@webcomponents/scoped-custom-element-registry/-/scoped-custom-element-registry-0.0.3.tgz#774591a886b0b0e4914717273ba53fd8d5657522" integrity sha512-lpSzgDCGbM99dytb3+J3Suo4+Bk1E13MPnWB42JK8GwxSAxFz+tC7TTv2hhDSIE2IirGNKNKCf3m08ecu6eAsQ== -"@whatwg-node/events@^0.0.3": - version "0.0.3" - resolved "https://registry.yarnpkg.com/@whatwg-node/events/-/events-0.0.3.tgz#13a65dd4f5893f55280f766e29ae48074927acad" - integrity sha512-IqnKIDWfXBJkvy/k6tzskWTc2NK3LcqHlb+KHGCrjOCH4jfQckRX0NAiIcC/vIqQkzLYw2r2CTSwAxcrtcD6lA== - -"@whatwg-node/fetch@^0.8.0", "@whatwg-node/fetch@^0.8.1": - version "0.8.8" - resolved "https://registry.yarnpkg.com/@whatwg-node/fetch/-/fetch-0.8.8.tgz#48c6ad0c6b7951a73e812f09dd22d75e9fa18cae" - integrity sha512-CdcjGC2vdKhc13KKxgsc6/616BQ7ooDIgPeTuAiE8qfCnS0mGzcfCOoZXypQSz73nxI+GWc7ZReIAVhxoE1KCg== - dependencies: - "@peculiar/webcrypto" "^1.4.0" - "@whatwg-node/node-fetch" "^0.3.6" - busboy "^1.6.0" - urlpattern-polyfill "^8.0.0" - web-streams-polyfill "^3.2.1" - -"@whatwg-node/node-fetch@^0.3.6": - version "0.3.6" - resolved "https://registry.yarnpkg.com/@whatwg-node/node-fetch/-/node-fetch-0.3.6.tgz#e28816955f359916e2d830b68a64493124faa6d0" - integrity sha512-w9wKgDO4C95qnXZRwZTfCmLWqyRnooGjcIwG0wADWjw9/HN0p7dtvtgSvItZtUyNteEvgTrd8QojNEqV6DAGTA== - dependencies: - "@whatwg-node/events" "^0.0.3" - busboy "^1.6.0" - fast-querystring "^1.1.1" - fast-url-parser "^1.1.3" - tslib "^2.3.1" - "@xtuc/ieee754@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" @@ -7939,15 +7693,6 @@ asap@^2.0.3, asap@~2.0.3: resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== -asn1js@^3.0.1, asn1js@^3.0.5: - version "3.0.5" - resolved "https://registry.yarnpkg.com/asn1js/-/asn1js-3.0.5.tgz#5ea36820443dbefb51cc7f88a2ebb5b462114f38" - integrity sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ== - dependencies: - pvtsutils "^1.3.2" - pvutils "^1.1.3" - tslib "^2.4.0" - assert@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/assert/-/assert-2.0.0.tgz#95fc1c616d48713510680f2eaf2d10dd22e02d32" @@ -8657,7 +8402,7 @@ builtin-modules@^3.3.0: resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6" integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw== -busboy@^1.0.0, busboy@^1.6.0: +busboy@^1.0.0: version "1.6.0" resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893" integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA== @@ -9810,16 +9555,6 @@ cors@^2.8.5, cors@~2.8.5: object-assign "^4" vary "^1" -cosmiconfig@8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.0.0.tgz#e9feae014eab580f858f8a0288f38997a7bebe97" - integrity sha512-da1EafcpH6b/TD8vDRaWV7xFINlHlF6zKsGwS1TsuVJTZRkquaS5HTMq7uq6h31619QjbsYl21gVDOm32KM1vQ== - dependencies: - import-fresh "^3.2.1" - js-yaml "^4.1.0" - parse-json "^5.0.0" - path-type "^4.0.0" - cosmiconfig@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-6.0.0.tgz#da4fee853c52f6b1e6935f41c1a2fc50bd4a9982" @@ -10214,11 +9949,6 @@ dataloader@^1.4.0: resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-1.4.0.tgz#bca11d867f5d3f1b9ed9f737bd15970c65dff5c8" integrity sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw== -dataloader@^2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-2.2.2.tgz#216dc509b5abe39d43a9b9d97e6e5e473dfbe3e0" - integrity sha512-8YnDaaf7N3k/q5HnTJVuzSyLETjoZjVmHc4AeKAzOvKHEFQKcn64OKBfzHYtE9zGjctNM7V9I0MfnUVLpi7M5g== - date-fns@^2.16.1, date-fns@^2.25.0: version "2.29.3" resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.29.3.tgz#27402d2fc67eb442b511b70bbdf98e6411cd68a8" @@ -10753,11 +10483,6 @@ dotenv@^8.1.0, dotenv@^8.6.0: resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b" integrity sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g== -dset@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/dset/-/dset-3.1.2.tgz#89c436ca6450398396dc6538ea00abc0c54cd45a" - integrity sha512-g/M9sqy3oHe477Ar4voQxWtaPIFw1jTdKZuomOjhCcBx9nHUNn0pu6NopuFFrTh/TRZIKEj+76vLWFu9BNKk+Q== - duplexer2@^0.1.2: version "0.1.4" resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" @@ -10822,11 +10547,6 @@ emoji-regex@^9.2.2: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== -emojis-list@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" - integrity sha512-knHEZMgs8BB+MInokmNTg/OyPlAddghe1YBgNwJBc5zsJi/uyIcXoSDsL/W9ymOsBoBGdPIHXYJ9+qKFwRwDng== - emojis-list@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" @@ -11986,11 +11706,6 @@ extglob@^2.0.4: snapdragon "^0.8.1" to-regex "^3.0.1" -extract-files@^11.0.0: - version "11.0.0" - resolved "https://registry.yarnpkg.com/extract-files/-/extract-files-11.0.0.tgz#b72d428712f787eef1f5193aff8ab5351ca8469a" - integrity sha512-FuoE1qtbJ4bBVvv94CC7s0oTnKUGvQs+Rjf1L2SJFfS+HTVVjhPFtehPdQ0JiGPqVNfSSZvL5yzHHQq2Z4WNhQ== - extract-zip@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a" @@ -12012,11 +11727,6 @@ extract-zip@^1.6.6: mkdirp "^0.5.4" yauzl "^2.10.0" -fast-decode-uri-component@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz#46f8b6c22b30ff7a81357d4f59abfae938202543" - integrity sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg== - fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -12060,26 +11770,12 @@ fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== -fast-querystring@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/fast-querystring/-/fast-querystring-1.1.2.tgz#a6d24937b4fc6f791b4ee31dcb6f53aeafb89f53" - integrity sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg== - dependencies: - fast-decode-uri-component "^1.0.1" - -fast-url-parser@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/fast-url-parser/-/fast-url-parser-1.1.3.tgz#f4af3ea9f34d8a271cf58ad2b3759f431f0b318d" - integrity sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ== - dependencies: - punycode "^1.3.2" - -fast-xml-parser@^3.12.13: - version "3.21.1" - resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-3.21.1.tgz#152a1d51d445380f7046b304672dd55d15c9e736" - integrity sha512-FTFVjYoBOZTJekiUsawGsSYV9QL0A+zDYCRj7y34IO6Jg+2IMYEtQa+bbictpdpV8dHxXywqU7C0gRDEOFtBFg== +fast-xml-parser@^4.2.4: + version "4.2.7" + resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.2.7.tgz#871f2ca299dc4334b29f8da3658c164e68395167" + integrity sha512-J8r6BriSLO1uj2miOk1NW0YVm8AGOOu3Si2HQp/cSmo6EA4m3fcwu2WKjJ4RK9wMLBtg69y1kS8baDiQBR41Ig== dependencies: - strnum "^1.0.4" + strnum "^1.0.5" fastest-levenshtein@^1.0.12, fastest-levenshtein@^1.0.16: version "1.0.16" @@ -13516,23 +13212,6 @@ graphql-compose@^9.0.7: dependencies: graphql-type-json "0.3.2" -graphql-config@^4.1.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/graphql-config/-/graphql-config-4.5.0.tgz#257c2338950b8dce295a27f75c5f6c39f8f777b2" - integrity sha512-x6D0/cftpLUJ0Ch1e5sj1TZn6Wcxx4oMfmhaG9shM0DKajA9iR+j1z86GSTQ19fShbGvrSSvbIQsHku6aQ6BBw== - dependencies: - "@graphql-tools/graphql-file-loader" "^7.3.7" - "@graphql-tools/json-file-loader" "^7.3.7" - "@graphql-tools/load" "^7.5.5" - "@graphql-tools/merge" "^8.2.6" - "@graphql-tools/url-loader" "^7.9.7" - "@graphql-tools/utils" "^9.0.0" - cosmiconfig "8.0.0" - jiti "1.17.1" - minimatch "4.2.3" - string-env-interpolation "1.0.1" - tslib "^2.4.0" - graphql-playground-html@^1.6.30: version "1.6.30" resolved "https://registry.yarnpkg.com/graphql-playground-html/-/graphql-playground-html-1.6.30.tgz#14c2a8eb7fc17bfeb1a746bbb28a11e34bf0b391" @@ -13559,11 +13238,6 @@ graphql-type-json@0.3.2: resolved "https://registry.yarnpkg.com/graphql-type-json/-/graphql-type-json-0.3.2.tgz#f53a851dbfe07bd1c8157d24150064baab41e115" integrity sha512-J+vjof74oMlCWXSvt0DOf2APEdZOCdubEvGDUAlqH//VBYcOYsGgRW7Xzorr44LvkjiuvecWc8fChxuZZbChtg== -graphql-ws@5.12.1: - version "5.12.1" - resolved "https://registry.yarnpkg.com/graphql-ws/-/graphql-ws-5.12.1.tgz#c62d5ac54dbd409cc6520b0b39de374b3d59d0dd" - integrity sha512-umt4f5NnMK46ChM2coO36PTFhHouBrK9stWWBczERguwYrGnPNxJ9dimU6IyOBfOkC6Izhkg4H8+F51W/8CYDg== - graphql@^15.7.2: version "15.8.0" resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.8.0.tgz#33410e96b012fa3bdb1091cc99a94769db212b38" @@ -14064,7 +13738,7 @@ ignore@^5.1.4, ignore@^5.1.8, ignore@^5.1.9, ignore@^5.2.0, ignore@^5.2.4: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== -immer@^9.0.6, immer@^9.0.7: +immer@^9.0.7: version "9.0.21" resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.21.tgz#1e025ea31a40f24fb064f1fef23e931496330176" integrity sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA== @@ -14879,11 +14553,6 @@ isobject@^3.0.0, isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== -isomorphic-ws@5.0.0, isomorphic-ws@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz#e5529148912ecb9b451b46ed44d53dae1ce04bbf" - integrity sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw== - istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" @@ -15430,11 +15099,6 @@ jimp-compact@^0.16.1-2: resolved "https://registry.yarnpkg.com/jimp-compact/-/jimp-compact-0.16.1-2.tgz#a82ff9a5a81f15a4b61b5e2e50fae6a43305e5a9" integrity sha512-b2A3rRT1TITzqmaO70U2/uunCh43BQVq7BfRwGPkD5xj8/WZsR3sPTy9DENt+dNZGsel3zBEm1UtYegUxjZW7A== -jiti@1.17.1: - version "1.17.1" - resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.17.1.tgz#264daa43ee89a03e8be28c3d712ccc4eb9f1e8ed" - integrity sha512-NZIITw8uZQFuzQimqjUxIrIcEdxYDFIe/0xYfIlVXTkiBjjyBEvgasj5bb0/cHtPRD/NziPbT312sFrkI5ALpw== - jiti@^1.18.2: version "1.18.2" resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.18.2.tgz#80c3ef3d486ebf2450d9335122b32d121f2a83cd" @@ -15593,19 +15257,7 @@ json-stable-stringify-without-jsonify@^1.0.1: resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== -json5@2.x, json5@^2.1.2, json5@^2.1.3, json5@^2.2.0, json5@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" - integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== - -json5@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" - integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== - dependencies: - minimist "^1.2.0" - -json5@^2.2.2: +json5@2.x, json5@^1.0.1, json5@^2.1.2, json5@^2.1.3, json5@^2.2.0, json5@^2.2.1, json5@^2.2.2: version "2.2.3" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== @@ -16043,38 +15695,15 @@ loader-runner@^4.2.0: resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== -loader-utils@1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7" - integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA== - dependencies: - big.js "^5.2.2" - emojis-list "^2.0.0" - json5 "^1.0.1" - -loader-utils@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" - integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== - dependencies: - big.js "^5.2.2" - emojis-list "^3.0.0" - json5 "^1.0.1" - -loader-utils@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.3.tgz#d4b15b8504c63d1fc3f2ade52d41bc8459d6ede1" - integrity sha512-THWqIsn8QRnvLl0shHYVBN9syumU8pYWEHPTmkiVGd+7K5eFNVSY6AJhRvgGF70gg1Dz+l/k8WicvFCxdEs60A== +loader-utils@1.2.3, loader-utils@^1.4.0, loader-utils@^2.0.0, loader-utils@^2.0.3, loader-utils@^3.2.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c" + integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw== dependencies: big.js "^5.2.2" emojis-list "^3.0.0" json5 "^2.1.2" -loader-utils@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-3.2.0.tgz#bcecc51a7898bee7473d4bc6b845b23af8304d4f" - integrity sha512-HVl9ZqccQihZ7JM85dco1MvO9G+ONvxoGa9rkhzFsneGLKSUg1gJf9bWzhRhcvm2qChhWpebQhP44qxjKIUCaQ== - locate-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" @@ -16642,10 +16271,10 @@ markdown-toc@^1.2.0: repeat-string "^1.6.1" strip-color "^0.1.0" -marked@^0.6.2, marked@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/marked/-/marked-0.7.0.tgz#b64201f051d271b1edc10a04d1ae9b74bb8e5c0e" - integrity sha512-c+yYdCZJQrsRjTPhUx7VKkApw9bwDkNbHUKo1ovgcfDjb2kc8rLuRbIFyXL5WOEUwzSSKo3IXpph2K6DqB/KZg== +marked@^0.6.2, marked@^4.0.10: + version "4.3.0" + resolved "https://registry.yarnpkg.com/marked/-/marked-4.3.0.tgz#796362821b019f734054582038b116481b456cf3" + integrity sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A== marky@^1.2.2: version "1.2.5" @@ -16890,11 +16519,6 @@ merge2@^1.2.3, merge2@^1.3.0, merge2@^1.4.1: resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== -meros@^1.2.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/meros/-/meros-1.3.0.tgz#c617d2092739d55286bf618129280f362e6242f2" - integrity sha512-2BNGOimxEz5hmjUG2FwoxCt5HN7BXdaWyFqEwxPTrJzVdABtrL4TiHTcsWSFAxPQ/tOnEaQEJh3qWq71QRMY+w== - methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" @@ -17007,13 +16631,6 @@ minify-html-literals@^1.3.5: magic-string "^0.25.0" parse-literals "^1.2.1" -minimatch@4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-4.2.3.tgz#b4dcece1d674dee104bb0fb833ebb85a78cbbca6" - integrity sha512-lIUdtK5hdofgCTu3aT0sOaHsYR37viUuIc0rwnnDXImbwFRcumyLMeZaM0t0I/fgxS6s6JMfu0rLD1Wz9pv1ng== - dependencies: - brace-expansion "^1.1.7" - minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" @@ -17345,7 +16962,7 @@ node-fetch-native@^1.0.2: resolved "https://registry.yarnpkg.com/node-fetch-native/-/node-fetch-native-1.2.0.tgz#13ec6df98f33168958dbfb6945f10aedf42e7ea8" integrity sha512-5IAMBTl9p6PaAjYCnMv5FmqIF6GcZnawAVnzaCG0rX2aYZJ4CxEkZNtVPuTRug7fL7wyM5BQYTlAzcyMPi6oTQ== -node-fetch@2.6.7, node-fetch@^2.5.0, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.6, node-fetch@^2.6.7: +node-fetch@2.6.7, node-fetch@^2.5.0, node-fetch@^2.6.0, node-fetch@^2.6.6, node-fetch@^2.6.7: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== @@ -18257,7 +17874,7 @@ playwright-core@1.35.0: resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.35.0.tgz#b7871b742b4a5c8714b7fa2f570c280a061cb414" integrity sha512-muMXyPmIx/2DPrCHOD1H1ePT01o7OdKxKj2ebmCAYvqhUy+Y1bpal7B0rdoxros7YrXI294JT/DWw2LqyiqTPA== -playwright@^1.17.1, playwright@^1.22.2: +playwright@^1.22.2: version "1.35.0" resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.35.0.tgz#4e3b3ea2495d6fd671700f77b2f97b3adedf80f1" integrity sha512-xhFhsoBmKPQfj3dM+HbIiFVlqRCZp2rwdJd/QFd9YBuidabo3TkVv0iuxPQ4vZoMwtSI7qzjY93f5ohdC97hww== @@ -19090,11 +18707,6 @@ punycode@1.3.2: resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" integrity sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw== -punycode@^1.3.2: - version "1.4.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" - integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ== - punycode@^2.1.0, punycode@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" @@ -19140,18 +18752,6 @@ puppeteer-core@^2.1.1: rimraf "^2.6.1" ws "^6.1.0" -pvtsutils@^1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/pvtsutils/-/pvtsutils-1.3.2.tgz#9f8570d132cdd3c27ab7d51a2799239bf8d8d5de" - integrity sha512-+Ipe2iNUyrZz+8K/2IOo+kKikdtfhRKzNpQbruF2URmqPtoqAs8g3xS7TJvFF2GcPXjh7DkqMnpVveRFq4PgEQ== - dependencies: - tslib "^2.4.0" - -pvutils@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/pvutils/-/pvutils-1.1.3.tgz#f35fc1d27e7cd3dfbd39c0826d173e806a03f5a3" - integrity sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ== - q@1.*, q@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" @@ -20386,7 +19986,7 @@ sassdoc-theme-default@^2.8.3: nunjucks "^3.1.7" sassdoc-extras "^2.5.0" -sassdoc@^2.7.3: +sassdoc@^2.7.4: version "2.7.4" resolved "https://registry.yarnpkg.com/sassdoc/-/sassdoc-2.7.4.tgz#9803a3e69f1e31847809c8b3f5044d4740960bc9" integrity sha512-/HEjX9pMILkePyC4ZKGhkLqZHJZpsTxFwQIQNsLhV4XHiPKoWHrSmam1pMntM79tcdtBII3JX7ShfyZjHnrkyw== @@ -21166,11 +20766,6 @@ string-argv@^0.3.1: resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da" integrity sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg== -string-env-interpolation@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/string-env-interpolation/-/string-env-interpolation-1.0.1.tgz#ad4397ae4ac53fe6c91d1402ad6f6a52862c7152" - integrity sha512-78lwMoCcn0nNu8LszbP1UA7g55OeE4v7rCeWnM5B453rnNr4aq+5it3FEYtZrSEiMvHZOZ9Jlqb0OD0M2VInqg== - string-length@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" @@ -21453,7 +21048,7 @@ strip-outer@^1.0.1: dependencies: escape-string-regexp "^1.0.2" -strnum@^1.0.4: +strnum@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/strnum/-/strnum-1.0.5.tgz#5c4e829fe15ad4ff0d20c3db5ac97b73c9b072db" integrity sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA== @@ -21673,15 +21268,15 @@ svg-tags@^1.0.0: resolved "https://registry.yarnpkg.com/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764" integrity sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA== -svglint@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/svglint/-/svglint-2.2.0.tgz#e456d12532c6840aa1e87f60ebb0839ebd05dd27" - integrity sha512-0Cd0Mi87QPU+VT0TGXNtLPRFOtT5b92APGmrI7iHfUqTQSquRaIDHm1rbU6J9aHCNSVitKgPVaGMCV9ykwJiiQ== +svglint@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/svglint/-/svglint-2.4.0.tgz#e43f81c2b30fddfe62bfd6f7672157344a993be5" + integrity sha512-aBLGPeVxojzcagzYs+2tV6Fw1ToHY9s5LhCsj0MRCiliy0sZ20ru7B8F/DHfUMMk1gy6itlE01adwuc58QBhDQ== dependencies: ansi-regex "^5.0.1" chalk "^5.0.0" cheerio "^1.0.0-rc.6" - fast-xml-parser "^3.12.13" + fast-xml-parser "^4.2.4" glob "^7.1.2" htmlparser2 "^3.9.1" log-update "^5.0.0" @@ -22225,11 +21820,6 @@ tslib@^2, tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.1, resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== -tslib@^2.5.0: - version "2.5.3" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.3.tgz#24944ba2d990940e6e982c4bea147aba80209913" - integrity sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w== - tsscmp@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/tsscmp/-/tsscmp-1.0.6.tgz#85b99583ac3589ec4bfef825b5000aa911d605eb" @@ -22860,11 +22450,6 @@ url@^0.11.0: punycode "1.3.2" querystring "0.2.0" -urlpattern-polyfill@^8.0.0: - version "8.0.2" - resolved "https://registry.yarnpkg.com/urlpattern-polyfill/-/urlpattern-polyfill-8.0.2.tgz#99f096e35eff8bf4b5a2aa7d58a1523d6ebc7ce5" - integrity sha512-Qp95D4TPJl1kC9SKigDcqgyM2VDVO4RiJc2d4qe5GrYm+zbIQCWWKAFaJNQ4BhdFeDGwBmAxqJBwWSJDb9T3BQ== - use-callback-ref@^1.3.0: version "1.3.0" resolved "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.0.tgz#772199899b9c9a50526fedc4993fc7fa1f7e32d5" @@ -22984,11 +22569,6 @@ value-or-promise@1.0.11: resolved "https://registry.yarnpkg.com/value-or-promise/-/value-or-promise-1.0.11.tgz#3e90299af31dd014fe843fe309cefa7c1d94b140" integrity sha512-41BrgH+dIbCFXClcSapVs5M6GkENd3gQOJpEfPDNa71LsUGMXDL0jMWpI/Rh7WhX+Aalfz2TTS3Zt5pUsbnhLg== -value-or-promise@^1.0.11, value-or-promise@^1.0.12: - version "1.0.12" - resolved "https://registry.yarnpkg.com/value-or-promise/-/value-or-promise-1.0.12.tgz#0e5abfeec70148c78460a849f6b003ea7986f15c" - integrity sha512-Z6Uz+TYwEqE7ZN50gwn+1LCVo9ZVrpxRPOhOLnncYkY1ZzOYtrX8Fwf/rFktZ8R5mJms6EZf5TqNOMeZmnPq9Q== - vary@^1, vary@^1.1.2, vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" @@ -23239,27 +22819,11 @@ web-namespaces@^1.0.0: resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-1.1.4.tgz#bc98a3de60dadd7faefc403d1076d529f5e030ec" integrity sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw== -web-streams-polyfill@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz#71c2718c52b45fd49dbeee88634b3a60ceab42a6" - integrity sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q== - web-vitals@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/web-vitals/-/web-vitals-1.1.2.tgz#06535308168986096239aa84716e68b4c6ae6d1c" integrity sha512-PFMKIY+bRSXlMxVAQ+m2aw9c/ioUYfDgrYot0YUa+/xa0sakubWhSDyxAKwzymvXVdF4CZI71g06W+mqhzu6ig== -webcrypto-core@^1.7.7: - version "1.7.7" - resolved "https://registry.yarnpkg.com/webcrypto-core/-/webcrypto-core-1.7.7.tgz#06f24b3498463e570fed64d7cab149e5437b162c" - integrity sha512-7FjigXNsBfopEj+5DV2nhNpfic2vumtjjgPmeDKk45z+MJwXKKfhPB7118Pfzrmh4jqOMST6Ch37iPAHoImg5g== - dependencies: - "@peculiar/asn1-schema" "^2.3.6" - "@peculiar/json-schema" "^1.1.12" - asn1js "^3.0.1" - pvtsutils "^1.3.2" - tslib "^2.4.0" - webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" @@ -23700,7 +23264,7 @@ write-file-atomic@^5.0.1: imurmurhash "^0.1.4" signal-exit "^4.0.1" -ws@8.13.0, ws@^8.12.0: +ws@8.13.0: version "8.13.0" resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== From e02030087baffbc9685f39f1f77725d1ba5e10bc Mon Sep 17 00:00:00 2001 From: Dane Hillard Date: Wed, 6 Sep 2023 16:44:06 -0400 Subject: [PATCH 08/19] Tokens: improve glacier-blue-40 AAA color contrast (#606) * fix(tokens): glacier-blue-40 AAA color contrast * chore: add changeset --- .changeset/fresh-wolves-rule.md | 5 +++++ packages/pharos/tokens/color/base.json | 8 ++++---- 2 files changed, 9 insertions(+), 4 deletions(-) create mode 100644 .changeset/fresh-wolves-rule.md diff --git a/.changeset/fresh-wolves-rule.md b/.changeset/fresh-wolves-rule.md new file mode 100644 index 000000000..2a13fc0af --- /dev/null +++ b/.changeset/fresh-wolves-rule.md @@ -0,0 +1,5 @@ +--- +'@ithaka/pharos': patch +--- + +Increase color contrast for glacier blue 40 diff --git a/packages/pharos/tokens/color/base.json b/packages/pharos/tokens/color/base.json index 9fe998ab6..a9aed1553 100644 --- a/packages/pharos/tokens/color/base.json +++ b/packages/pharos/tokens/color/base.json @@ -8,7 +8,7 @@ }, "Glacier blue": { "base": {"value": "hsl(187.4, 75.6%, 51.8%)", "group": "primary", "palette": "secondary"}, - "40": { "value": "hsl(190, 89%, 40%)", "group": "tint", "palette": "secondary", "order": "1" }, + "40": { "value": "hsl(190, 89%, 39%)", "group": "tint", "palette": "secondary", "order": "1" }, "70": { "value": "hsl(187, 75%, 70%)", "group": "tint", "palette": "secondary", "order": "1" }, "80": { "value": "hsl(188, 47%, 80%)", "group": "tint", "palette": "secondary", "order": "2" }, "90": { "value": "hsl(186.3,37.3%,90%)", "group": "tint", "palette": "secondary", "order": "2" } @@ -35,11 +35,11 @@ "White": { "value": "hsl(0, 0%, 100%)", "palette": "primary" }, "Yellow": { "base": { "value": "hsl(37.1, 95.5%, 56.5%)", "group": "", "palette": "feedback", "order": "1"}, - "97": { "value": "hsl(38.2,73.3%,97.1%)", "group": "", "palette": "feedback", "order": "1" } + "97": { "value": "hsl(38.2, 73.3%, 97.1%)", "group": "", "palette": "feedback", "order": "1" } }, - "Green": { + "Green": { "base": { "value": "hsl(137.6, 54.8%, 32.9%)", "group": "", "palette": "feedback", "order": "1"}, - "93": { "value": "hsl(135,22.2%,92.9%)", "group": "", "palette": "feedback", "order": "1" } + "93": { "value": "hsl(135, 22.2%, 92.9%)", "group": "", "palette": "feedback", "order": "1" } }, "interactive": { "primary": { From 2f1708eb7c8f9465971c6d55bc9847baa3fca067 Mon Sep 17 00:00:00 2001 From: Mat Harris Date: Wed, 6 Sep 2023 20:19:08 -0400 Subject: [PATCH 09/19] docs: add @sirrah-tam as a contributor (#607) --- .all-contributorsrc | 9 +++++++++ README.md | 1 + 2 files changed, 10 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 3d0970223..5098cd451 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -308,6 +308,15 @@ "bug", "code" ] + }, + { + "login": "sirrah-tam", + "name": "Mat Harris", + "avatar_url": "https://avatars.githubusercontent.com/u/6874453?v=4", + "profile": "https://matharris.dev/", + "contributions": [ + "a11y" + ] } ], "contributorsPerLine": 7 diff --git a/README.md b/README.md index c74be5daa..40809e835 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d Hassan Tanveer
Hassan Tanveer

💻 mariadevadoss
mariadevadoss

💻 Brent Swisher
Brent Swisher

🐛 💻 + Mat Harris
Mat Harris

️️️️♿️ From 5d15316632da788f692ec731a2aae765e6785c21 Mon Sep 17 00:00:00 2001 From: Mike Iden Date: Tue, 12 Sep 2023 14:51:19 -0400 Subject: [PATCH 10/19] Sheet: Component Contribution (#603) * feat(sheet): initial sheet implementation * feat(sheet): basic sheet toggle * feat(sheet): conditionally render close button * feat(sheet): add swipe handler to the sheet * feat(sheet): update handler and add box shadow * feat(sheet): add unit tests * feat(sheet): update styles * feat(sheet): update styles * feat(sheet): update styles * feat(sheet): update styles * feat(sheet): add aria label to the sheet * feat(sheet): add footer to storybook * feat(sheet): change color and fix typo * docs(sheet): add changeset * feat(sheet): clean up * feat(sheet): clean up --------- Co-authored-by: Jialin He Co-authored-by: Jialin He <38861633+jialin-he@users.noreply.github.com> --- .changeset/blue-masks-retire.md | 5 + .storybook/initComponents.js | 2 + .../sheet/PharosSheet.react.stories.jsx | 35 ++ .../src/components/sheet/pharos-sheet.scss | 144 +++++++ .../src/components/sheet/pharos-sheet.test.ts | 121 ++++++ .../src/components/sheet/pharos-sheet.ts | 363 ++++++++++++++++++ .../sheet/pharos-sheet.wc.stories.jsx | 43 +++ packages/pharos/src/index.ts | 1 + packages/pharos/src/test/initComponents.ts | 2 + 9 files changed, 716 insertions(+) create mode 100644 .changeset/blue-masks-retire.md create mode 100644 packages/pharos/src/components/sheet/PharosSheet.react.stories.jsx create mode 100644 packages/pharos/src/components/sheet/pharos-sheet.scss create mode 100644 packages/pharos/src/components/sheet/pharos-sheet.test.ts create mode 100644 packages/pharos/src/components/sheet/pharos-sheet.ts create mode 100644 packages/pharos/src/components/sheet/pharos-sheet.wc.stories.jsx diff --git a/.changeset/blue-masks-retire.md b/.changeset/blue-masks-retire.md new file mode 100644 index 000000000..aeb302036 --- /dev/null +++ b/.changeset/blue-masks-retire.md @@ -0,0 +1,5 @@ +--- +'@ithaka/pharos': minor +--- + +Add sheet component diff --git a/.storybook/initComponents.js b/.storybook/initComponents.js index 24962b79b..6ec0295bb 100644 --- a/.storybook/initComponents.js +++ b/.storybook/initComponents.js @@ -27,6 +27,7 @@ import { PharosRadioButton, PharosRadioGroup, PharosSelect, + PharosSheet, PharosSidenav, PharosSidenavButton, PharosSidenavLink, @@ -75,6 +76,7 @@ registerComponents('storybook', [ PharosRadioButton, PharosRadioGroup, PharosSelect, + PharosSheet, PharosSidenav, PharosSidenavButton, PharosSidenavLink, diff --git a/packages/pharos/src/components/sheet/PharosSheet.react.stories.jsx b/packages/pharos/src/components/sheet/PharosSheet.react.stories.jsx new file mode 100644 index 000000000..1ab497bce --- /dev/null +++ b/packages/pharos/src/components/sheet/PharosSheet.react.stories.jsx @@ -0,0 +1,35 @@ +import { PharosSheet } from '../../react-components/sheet/pharos-sheet'; +import { configureDocsPage } from '@config/docsPageConfig'; +import { PharosContext } from '../../utils/PharosContext'; +import { PharosButton } from '../../react-components'; + +export default { + title: 'Components/Sheet', + component: PharosSheet, + decorators: [ + (Story) => ( + + + + ), + ], + parameters: { + docs: { + page: configureDocsPage('sheet'), + }, + }, +}; + +export const Base = { + render: () => , + args: {}, +}; + +export const WithFooter = { + render: () => ( + +
Lorem ipsum dolor sit amet
+ Do something +
+ ), +}; diff --git a/packages/pharos/src/components/sheet/pharos-sheet.scss b/packages/pharos/src/components/sheet/pharos-sheet.scss new file mode 100644 index 000000000..99d6b385d --- /dev/null +++ b/packages/pharos/src/components/sheet/pharos-sheet.scss @@ -0,0 +1,144 @@ +@use '../../utils/scss/mixins'; + +:host { + display: block; + position: fixed; + z-index: 9000; + pointer-events: none; + inset: 0; + contain: strict; +} + +.sheet__overlay { + display: block; + justify-content: center; + align-items: center; + position: fixed; + left: 0; + top: 0; + width: 100vw; + background-color: rgb(0 0 0 / 0); + visibility: hidden; + transition: visibility var(--pharos-transition-duration-default) ease-in-out, + background-color var(--pharos-transition-duration-default) ease-in-out; + overflow-y: visible; + transition-duration: var(--pharos-transition-duration-long); + height: 100%; +} + +.sheet__dialog { + position: relative; + min-width: 100vw; + max-height: 100vh; + height: 100%; + top: 100%; + pointer-events: none; + z-index: 9001; + visibility: hidden; + transition: visibility var(--pharos-transition-duration-long) linear + var(--pharos-transition-duration-long), + top var(--pharos-transition-duration-long) ease-in-out; +} + +:host([open]) { + pointer-events: auto; + + .sheet__overlay { + background-color: rgb(0 0 0 / 0.5); + visibility: visible; + } + + .sheet__dialog { + visibility: visible; + top: 0; + transition: visibility 0s linear 0s, top var(--pharos-transition-duration-long) ease-in-out; + } +} + +.sheet__content { + @include mixins.font-base; + + position: absolute; + bottom: 0; + display: flex; + flex-direction: column; + justify-content: space-between; + height: 50%; + width: 100%; + pointer-events: auto; + overflow-y: scroll; + background-color: var(--pharos-color-white); + background-clip: padding-box; + border: 1px solid var(--pharos-color-marble-gray-base); + border-radius: var(--pharos-radius-base-standard); + outline: 0; + transition: height var(--pharos-transition-base); + box-shadow: 0 1px 2px 0 rgb(18 18 18 / 0.3), 0 -4px 8px 3px rgb(18 18 18 / 0.15); + + .sheet__wrapper { + padding: 0 var(--pharos-spacing-one-and-a-half-x); + } + + @include mixins.until(small) { + .sheet__wrapper { + padding: 0 20px; + } + } + + .sheet__handle { + background: var(--pharos-color-marble-gray-80); + width: var(--pharos-spacing-three-and-a-half-x); + height: 4px; + border-radius: 2px; + margin: var(--pharos-spacing-one-half-x) auto 0 auto; + box-sizing: border-box; + cursor: grab; + } +} + +.sheet__body { + position: relative; + flex: 1 1 auto; + margin-top: 0.375rem; +} + +.sheet__header { + display: flex; + align-items: flex-start; + justify-content: space-between; + margin-top: var(--pharos-spacing-one-and-a-half-x); +} + +.sheet__footer { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: flex-end; + padding: var(--pharos-spacing-1-x) var(--pharos-spacing-one-and-a-half-x); + + @at-root #{&}--empty { + padding: 0 0 var(--pharos-spacing-2-x); + } +} + +@include mixins.until(small) { + .sheet__footer { + padding: var(--pharos-spacing-1-x) 1.25rem; + } +} + +slot[name='footer']::slotted(*) { + margin-left: var(--pharos-spacing-1-x); +} + +:host([expanded]) { + .sheet__content { + height: 100%; + } +} + +:host([footer-divider]) { + .sheet__footer { + border-top: 1px solid var(--pharos-color-marble-gray-base); + } +} diff --git a/packages/pharos/src/components/sheet/pharos-sheet.test.ts b/packages/pharos/src/components/sheet/pharos-sheet.test.ts new file mode 100644 index 000000000..060fb0387 --- /dev/null +++ b/packages/pharos/src/components/sheet/pharos-sheet.test.ts @@ -0,0 +1,121 @@ +import { fixture, expect } from '@open-wc/testing'; +import { html } from 'lit/static-html.js'; +import sinon from 'sinon'; +import type { SinonSpy } from 'sinon'; +import type { PharosSheet } from './pharos-sheet'; +import type { PharosButton } from '../button/pharos-button'; + +describe('pharos-sheet', () => { + let component: PharosSheet, logSpy: SinonSpy; + + beforeEach(async () => { + component = await fixture( + html` My Sheet ` + ); + }); + + before(() => { + logSpy = sinon.spy(console, 'error'); + }); + + after(() => { + logSpy.restore(); + }); + + const getSimpleSheet = () => { + return html` + +
I am sheet contents
+
+ `; + }; + + it('is accessible', async () => { + await expect(component).to.be.accessible(); + }); + + it('is accessible when open', async () => { + component.open = true; + await component.updateComplete; + + await expect(component).to.be.accessible(); + }); + + it('opens when the element with matching attribute data-sheet-id is clicked', async () => { + const trigger = document.createElement('button'); + trigger.setAttribute('id', 'trigger'); + trigger.setAttribute('data-sheet-id', 'my-sheet'); + document.body.appendChild(trigger); + + component = await fixture(getSimpleSheet()); + + trigger.click(); + await component.updateComplete; + expect(component.open).to.be.true; + }); + + it('closes when the close button is pressed', async () => { + component.open = true; + await component.updateComplete; + + const closeButton = component.renderRoot.querySelector('#close-button') as PharosButton; + closeButton?.click(); + + await component.updateComplete; + expect(component.open).to.be.false; + }); + + it('returns focus back to the trigger element when close', async () => { + let activeElement = null; + const onFocusIn = (event: Event): void => { + activeElement = event.composedPath()[0]; + }; + document.addEventListener('focusin', onFocusIn); + + const trigger = document.createElement('button'); + trigger.setAttribute('id', 'trigger'); + trigger.setAttribute('data-sheet-id', 'my-sheet'); + document.body.appendChild(trigger); + + const button = document.querySelector('#trigger') as HTMLButtonElement; + button.click(); + button.focus(); + await component.updateComplete; + + component.open = false; + await component.updateComplete; + + expect(activeElement === button).to.be.true; + document.removeEventListener('focusin', onFocusIn); + }); + + it('fires a custom event pharos-sheet-opened when opened', async () => { + component = await fixture(getSimpleSheet()); + + let wasFired = false; + const handleOpened = (): void => { + wasFired = true; + }; + component.addEventListener('pharos-sheet-opened', handleOpened); + component.open = true; + await component.updateComplete; + + expect(wasFired).to.be.true; + }); + + it('fires a custom event pharos-sheet-closed when closed', async () => { + component = await fixture(getSimpleSheet()); + + let wasFired = false; + const handleClosed = (): void => { + wasFired = true; + }; + component.addEventListener('pharos-sheet-closed', handleClosed); + component.open = true; + await component.updateComplete; + + component.open = false; + await component.updateComplete; + expect(wasFired).to.be.true; + }); +}); diff --git a/packages/pharos/src/components/sheet/pharos-sheet.ts b/packages/pharos/src/components/sheet/pharos-sheet.ts new file mode 100644 index 000000000..0b45c7334 --- /dev/null +++ b/packages/pharos/src/components/sheet/pharos-sheet.ts @@ -0,0 +1,363 @@ +import { PharosElement } from '../base/pharos-element'; +import { html, nothing } from 'lit'; +import { property, state } from 'lit/decorators.js'; +import type { TemplateResult, CSSResultArray, PropertyValues } from 'lit'; +import { ifDefined } from 'lit/directives/if-defined.js'; +import { sheetStyles } from './pharos-sheet.css'; +import focusable from '../../utils/focusable'; + +import ScopedRegistryMixin from '../../utils/mixins/scoped-registry'; +import { PharosButton } from '../button/pharos-button'; +import { PharosHeading } from '../heading/pharos-heading'; +import { FocusTrap } from '@ithaka/focus-trap'; + +const CLOSE_BUTTONS = `[data-sheet-close],[data-pharos-component="PharosButton"]#close-button`; +const FOCUS_ELEMENT = `[data-sheet-focus]`; + +/** + * Pharos sheet component. + * + * @tag pharos-sheet + * + * @slot description - Content that describes the primary message or purpose of the sheet. + * @slot - Contains the content of the sheet body. + * @slot footer - Contains the content of the sheet footer. + * + * @fires pharos-sheet-open - Fires when the sheet is about to open - cancelable + * @fires pharos-sheet-opened - Fires when the sheet has opened + * @fires pharos-sheet-close - Fires when the sheet is about to close - cancelable + * @fires pharos-sheet-closed - Fires when the sheet has closed + * + */ +export class PharosSheet extends ScopedRegistryMixin(PharosElement) { + static elementDefinitions = { + 'pharos-button': PharosButton, + 'pharos-heading': PharosHeading, + 'focus-trap': FocusTrap, + }; + + /** + * Indicates if the sheet is open. + * @attr open + */ + @property({ type: Boolean, reflect: true }) + public open = false; + + /** + * Indicates if the sheet is expanded. + * @attr expanded + */ + @property({ type: Boolean, reflect: true }) + public expanded = false; + + /** + * Indicates if the sheet is allowed to expand. + * @attr enableExpansion + */ + @property({ type: Boolean, reflect: true }) + public enableExpansion = true; + + /** + * Indicates if the sheet contains close button. + * @attr hasClose + */ + @property({ type: Boolean, reflect: true }) + public hasClose = true; + + /** + * Text content for the sheet header + * @attr header + */ + @property({ type: String, reflect: true }) + public header = 'Sheet header'; + + /** + * Indicates if the sheet footer should contain a divider. + * @attr footer-divider + */ + @property({ type: Boolean, reflect: true, attribute: 'footer-divider' }) + public footerDivider = false; + + private _currentTrigger: Element | null = null; + + private _triggers!: NodeListOf; + + @state() + private _isFooterEmpty = true; + + private _startHeight = 0; + private _startY = 0; + private _isDragging = false; + + constructor() { + super(); + this._handleKeydown = this._handleKeydown.bind(this); + this._handleTriggerClick = this._handleTriggerClick.bind(this); + if (this.enableExpansion) { + this.addEventListener('touchend', this._handleDragEnd); + this.addEventListener('mouseup', this._handleDragEnd); + this.addEventListener('touchmove', this._handleTouchDragging); + this.addEventListener('mousemove', this._handleMouseDragging); + } + } + + public static override get styles(): CSSResultArray { + return [sheetStyles]; + } + + protected override firstUpdated(): void { + this._addTriggerListeners(); + } + + protected override update(changedProperties: PropertyValues): void { + super.update && super.update(changedProperties); + } + + protected override updated(changedProperties: PropertyValues): void { + if (changedProperties.has('open')) { + if (this.open) { + this._focusContents(); + } else { + this._returnTriggerFocus(); + } + + if (changedProperties.get('open') !== undefined) { + this._emitVisibilityChange(); + } + } + } + + override connectedCallback(): void { + super.connectedCallback && super.connectedCallback(); + document.addEventListener('keydown', this._handleKeydown); + + this._addTriggerListeners(); + } + + override disconnectedCallback(): void { + document.removeEventListener('keydown', this._handleKeydown); + this._triggers.forEach((trigger) => { + trigger.removeEventListener('click', this._handleTriggerClick); + }); + + super.disconnectedCallback && super.disconnectedCallback(); + } + + private _addTriggerListeners(): void { + const id = this.getAttribute('id'); + this._triggers = document.querySelectorAll(`[data-sheet-id="${id}"]`); + this._triggers.forEach((trigger) => { + trigger.addEventListener('click', this._handleTriggerClick); + }); + } + + private _closeSheet(trigger: EventTarget | null): void { + if (this.open) { + const details = { + bubbles: true, + composed: true, + detail: trigger, + }; + + if ( + this.dispatchEvent( + new CustomEvent('pharos-sheet-close', { + ...details, + cancelable: true, + }) + ) + ) { + this.open = false; + } + } + } + + private _openSheet(trigger: EventTarget | null): void { + if (!this.open) { + const details = { + bubbles: true, + composed: true, + detail: trigger, + }; + + if ( + this.dispatchEvent(new CustomEvent('pharos-sheet-open', { ...details, cancelable: true })) + ) { + this.open = true; + } + } + } + + private _handleKeydown(event: KeyboardEvent): void { + if ((event.key === 'Escape' || event.key === 'Esc') && this.open) { + event.stopPropagation(); + this._closeSheet(event.target); + } + } + + private _handleDialogClick(event: MouseEvent): void { + if ((event.target as Element).matches(CLOSE_BUTTONS)) { + this._closeSheet(event.target); + } + } + + private _handleMouseDragStart(event: MouseEvent): void { + if (!this.enableExpansion) return; + this._isDragging = true; + const sheetContent = this.shadowRoot?.querySelector(`.sheet__content`) as HTMLDivElement; + this._startHeight = sheetContent.clientHeight; + this._startY = event.pageY; + } + + private _handleTouchDragStart(event: TouchEvent): void { + if (!this.enableExpansion) return; + this._isDragging = true; + const sheetContent = this.shadowRoot?.querySelector(`.sheet__content`) as HTMLDivElement; + this._startHeight = sheetContent.clientHeight; + this._startY = event.touches?.[0].pageY; + } + + private _handleMouseDragging(event: MouseEvent): void { + if (this._isDragging) { + const delta = this._startY - event.pageY; + const newHeight = this._startHeight + delta; + const sheetContent = this.shadowRoot?.querySelector(`.sheet__content`) as HTMLDivElement; + sheetContent.style.height = `${newHeight}px`; + } + } + + private _handleTouchDragging(event: TouchEvent): void { + if (this._isDragging) { + const delta = this._startY - event.touches?.[0].pageY; + const newHeight = this._startHeight + delta; + const sheetContent = this.shadowRoot?.querySelector(`.sheet__content`) as HTMLDivElement; + sheetContent.style.height = `${newHeight}px`; + } + } + + private _handleDragEnd(): void { + this._isDragging = false; + const sheetContent = this.shadowRoot?.querySelector(`.sheet__content`) as HTMLDivElement; + const sheetHeight = sheetContent.clientHeight; + if (sheetHeight > this._startHeight) { + sheetContent.style.height = '100%'; + } else { + sheetContent.style.height = '50%'; + } + } + + private _handleTriggerClick(event: MouseEvent): void { + event.preventDefault(); + this._openSheet(event.target); + } + + private async _focusContents(): Promise { + this._currentTrigger = this.ownerDocument.activeElement; + const focusElement = this.querySelector(FOCUS_ELEMENT); + if (focusElement) { + await 0; + (focusElement as HTMLElement).focus(); + } else { + const tabbable = this.shadowRoot?.querySelector(focusable); + if (tabbable) { + await 0; + (tabbable as HTMLElement).focus(); + } + } + } + + private _returnTriggerFocus(): void { + if (this._currentTrigger && typeof (this._currentTrigger as HTMLElement).focus === 'function') { + (this._currentTrigger as HTMLElement).focus(); + this._currentTrigger = null; + } + } + + private _emitVisibilityChange() { + const details = { + bubbles: true, + composed: true, + }; + + if (this.open) { + this.dispatchEvent(new CustomEvent('pharos-sheet-opened', details)); + } else { + this.dispatchEvent(new CustomEvent('pharos-sheet-closed', details)); + } + } + + protected get descriptionContent(): TemplateResult | typeof nothing { + const descriptionSlot = [...this.children].filter((child) => child.slot === 'description'); + const content = descriptionSlot.length + ? html`
+ +
` + : nothing; + return content; + } + + protected get descriptionId(): string | undefined { + const descriptionSlot = [...this.children].filter((child) => child.slot === 'description'); + return descriptionSlot.length ? 'description' : undefined; + } + + private _handleFooterSlotchange(e: Event) { + this._isFooterEmpty = + (e.target as HTMLSlotElement).assignedNodes({ flatten: true }).length === 0; + } + + private _renderCloseButton(): TemplateResult | typeof nothing { + return this.hasClose + ? html` ` + : nothing; + } + + private _renderSheetHandle(): TemplateResult | typeof nothing { + return this.enableExpansion + ? html`
` + : nothing; + } + + protected override render(): TemplateResult { + return html` +
+ +
+ `; + } +} diff --git a/packages/pharos/src/components/sheet/pharos-sheet.wc.stories.jsx b/packages/pharos/src/components/sheet/pharos-sheet.wc.stories.jsx new file mode 100644 index 000000000..1da7e3712 --- /dev/null +++ b/packages/pharos/src/components/sheet/pharos-sheet.wc.stories.jsx @@ -0,0 +1,43 @@ +import { html } from 'lit'; + +import { configureDocsPage } from '@config/docsPageConfig'; + +export default { + title: 'Components/Sheet', + component: 'pharos-sheet', + parameters: { + docs: { + page: configureDocsPage('sheet'), + }, + options: { selectedPanel: 'addon-controls' }, + }, +}; + +export const Base = { + render: () => + html` +
+ + Click Me + + +
Lorem ipsum dolor sit amet
+
+
+ `, +}; + +export const WithFooter = { + render: () => + html` +
+ + Click Me + + +
Lorem ipsum dolor sit amet
+ Do something +
+
+ `, +}; diff --git a/packages/pharos/src/index.ts b/packages/pharos/src/index.ts index 7cb055afe..5b19b30c7 100644 --- a/packages/pharos/src/index.ts +++ b/packages/pharos/src/index.ts @@ -42,3 +42,4 @@ export { PharosImageCard } from './components/image-card/pharos-image-card'; export { PharosToggleButton } from './components/toggle-button-group/pharos-toggle-button'; export { PharosToggleButtonGroup } from './components/toggle-button-group/pharos-toggle-button-group'; export { PharosPopover } from './components/popover/pharos-popover'; +export { PharosSheet } from './components/sheet/pharos-sheet'; diff --git a/packages/pharos/src/test/initComponents.ts b/packages/pharos/src/test/initComponents.ts index 36c664ef7..209a1660d 100644 --- a/packages/pharos/src/test/initComponents.ts +++ b/packages/pharos/src/test/initComponents.ts @@ -27,6 +27,7 @@ import { PharosRadioButton, PharosRadioGroup, PharosSelect, + PharosSheet, PharosSidenav, PharosSidenavButton, PharosSidenavLink, @@ -75,6 +76,7 @@ registerComponents('test', [ PharosRadioButton, PharosRadioGroup, PharosSelect, + PharosSheet, PharosSidenav, PharosSidenavButton, PharosSidenavLink, From d762a0294cd940970c8bbaff8c97aae7e9bdae82 Mon Sep 17 00:00:00 2001 From: Mike Iden Date: Tue, 12 Sep 2023 15:48:43 -0400 Subject: [PATCH 11/19] Popover: Border radius styling enforcement (#610) * fix(popover): overflow hidden on popover to have child element adhere to the border radius * fix(popover): add changeset --- .changeset/chilly-mirrors-peel.md | 5 +++++ packages/pharos/src/components/popover/pharos-popover.scss | 1 + 2 files changed, 6 insertions(+) create mode 100644 .changeset/chilly-mirrors-peel.md diff --git a/.changeset/chilly-mirrors-peel.md b/.changeset/chilly-mirrors-peel.md new file mode 100644 index 000000000..64a5bdf12 --- /dev/null +++ b/.changeset/chilly-mirrors-peel.md @@ -0,0 +1,5 @@ +--- +'@ithaka/pharos': patch +--- + +Force popover border radius styling diff --git a/packages/pharos/src/components/popover/pharos-popover.scss b/packages/pharos/src/components/popover/pharos-popover.scss index 7b991dfad..11adb1768 100644 --- a/packages/pharos/src/components/popover/pharos-popover.scss +++ b/packages/pharos/src/components/popover/pharos-popover.scss @@ -22,6 +22,7 @@ transform: translate3d(0, 0, 0); transition: visibility 0s var(--pharos-transition-duration-short), opacity var(--pharos-transition-duration-short) linear; + overflow: hidden; } :host([open]) .popover { From 2a4659d3fa92d7b8dd072241aeddd82793c4b3db Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 12 Sep 2023 16:00:13 -0400 Subject: [PATCH 12/19] chore: version packages (#611) Co-authored-by: github-actions[bot] --- .changeset/blue-masks-retire.md | 5 ----- .changeset/chilly-mirrors-peel.md | 5 ----- .changeset/eleven-spoons-battle.md | 5 ----- .changeset/fresh-wolves-rule.md | 5 ----- packages/pharos/CHANGELOG.md | 14 ++++++++++++++ packages/pharos/package.json | 2 +- 6 files changed, 15 insertions(+), 21 deletions(-) delete mode 100644 .changeset/blue-masks-retire.md delete mode 100644 .changeset/chilly-mirrors-peel.md delete mode 100644 .changeset/eleven-spoons-battle.md delete mode 100644 .changeset/fresh-wolves-rule.md diff --git a/.changeset/blue-masks-retire.md b/.changeset/blue-masks-retire.md deleted file mode 100644 index aeb302036..000000000 --- a/.changeset/blue-masks-retire.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@ithaka/pharos': minor ---- - -Add sheet component diff --git a/.changeset/chilly-mirrors-peel.md b/.changeset/chilly-mirrors-peel.md deleted file mode 100644 index 64a5bdf12..000000000 --- a/.changeset/chilly-mirrors-peel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@ithaka/pharos': patch ---- - -Force popover border radius styling diff --git a/.changeset/eleven-spoons-battle.md b/.changeset/eleven-spoons-battle.md deleted file mode 100644 index 232c89f13..000000000 --- a/.changeset/eleven-spoons-battle.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@ithaka/pharos': patch ---- - -Add line rules to light variant of dropdown diff --git a/.changeset/fresh-wolves-rule.md b/.changeset/fresh-wolves-rule.md deleted file mode 100644 index 2a13fc0af..000000000 --- a/.changeset/fresh-wolves-rule.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@ithaka/pharos': patch ---- - -Increase color contrast for glacier blue 40 diff --git a/packages/pharos/CHANGELOG.md b/packages/pharos/CHANGELOG.md index 8a63d813b..edc1bd17c 100644 --- a/packages/pharos/CHANGELOG.md +++ b/packages/pharos/CHANGELOG.md @@ -1,5 +1,19 @@ # @ithaka/pharos +## 12.26.0 + +### Minor Changes + +- [#603](https://github.com/ithaka/pharos/pull/603) [`5d15316`](https://github.com/ithaka/pharos/commit/5d15316632da788f692ec731a2aae765e6785c21) Thanks [@michael-iden](https://github.com/michael-iden)! - Add sheet component + +### Patch Changes + +- [#610](https://github.com/ithaka/pharos/pull/610) [`d762a02`](https://github.com/ithaka/pharos/commit/d762a0294cd940970c8bbaff8c97aae7e9bdae82) Thanks [@michael-iden](https://github.com/michael-iden)! - Force popover border radius styling + +- [#599](https://github.com/ithaka/pharos/pull/599) [`834f7ab`](https://github.com/ithaka/pharos/commit/834f7ab8976f194bc641db244671338e7f7ab023) Thanks [@jialin-he](https://github.com/jialin-he)! - Add line rules to light variant of dropdown + +- [#606](https://github.com/ithaka/pharos/pull/606) [`e020300`](https://github.com/ithaka/pharos/commit/e02030087baffbc9685f39f1f77725d1ba5e10bc) Thanks [@daneah](https://github.com/daneah)! - Increase color contrast for glacier blue 40 + ## 12.25.0 ### Minor Changes diff --git a/packages/pharos/package.json b/packages/pharos/package.json index 32c8125e9..22340b582 100644 --- a/packages/pharos/package.json +++ b/packages/pharos/package.json @@ -3,7 +3,7 @@ "publishConfig": { "access": "public" }, - "version": "12.25.0", + "version": "12.26.0", "description": "Pharos web components for products and experiences", "files": [ "lib", From ef13fa18638f630f55716c60d81c9ba8d23c255d Mon Sep 17 00:00:00 2001 From: Jialin He <38861633+jialin-he@users.noreply.github.com> Date: Mon, 18 Sep 2023 09:29:00 -0400 Subject: [PATCH 13/19] Sheet: Add overflow for long content and remove footer (#613) * feat(sheet): add overflow for long content * feat(sheet): fix dragging issue * docs(sheet): add changeset * feat(sheet): no close button by default * feat(sheet): fix unit test * feat(sheet): remove additional styling to match popover * feat(sheet): clean up --- .changeset/fresh-parrots-judge.md | 5 ++ .../sheet/PharosSheet.react.stories.jsx | 30 ++++++-- .../src/components/sheet/pharos-sheet.scss | 42 ++--------- .../src/components/sheet/pharos-sheet.test.ts | 4 +- .../src/components/sheet/pharos-sheet.ts | 70 +++++++------------ .../sheet/pharos-sheet.wc.stories.jsx | 37 ++++++++-- 6 files changed, 98 insertions(+), 90 deletions(-) create mode 100644 .changeset/fresh-parrots-judge.md diff --git a/.changeset/fresh-parrots-judge.md b/.changeset/fresh-parrots-judge.md new file mode 100644 index 000000000..369f31321 --- /dev/null +++ b/.changeset/fresh-parrots-judge.md @@ -0,0 +1,5 @@ +--- +'@ithaka/pharos': patch +--- + +Add overflow to sheet diff --git a/packages/pharos/src/components/sheet/PharosSheet.react.stories.jsx b/packages/pharos/src/components/sheet/PharosSheet.react.stories.jsx index 1ab497bce..3f38ffafe 100644 --- a/packages/pharos/src/components/sheet/PharosSheet.react.stories.jsx +++ b/packages/pharos/src/components/sheet/PharosSheet.react.stories.jsx @@ -1,7 +1,6 @@ import { PharosSheet } from '../../react-components/sheet/pharos-sheet'; import { configureDocsPage } from '@config/docsPageConfig'; import { PharosContext } from '../../utils/PharosContext'; -import { PharosButton } from '../../react-components'; export default { title: 'Components/Sheet', @@ -25,11 +24,34 @@ export const Base = { args: {}, }; -export const WithFooter = { +export const WithClose = { render: () => ( - + +
Lorem ipsum dolor sit amet
+
+ ), +}; + +export const LongContent = { + render: () => ( + +
Lorem ipsum dolor sit amet
+
Lorem ipsum dolor sit amet
+
Lorem ipsum dolor sit amet
+
Lorem ipsum dolor sit amet
+
Lorem ipsum dolor sit amet
+
Lorem ipsum dolor sit amet
+
Lorem ipsum dolor sit amet
+
Lorem ipsum dolor sit amet
+
Lorem ipsum dolor sit amet
+
Lorem ipsum dolor sit amet
+
Lorem ipsum dolor sit amet
+
Lorem ipsum dolor sit amet
+
Lorem ipsum dolor sit amet
+
Lorem ipsum dolor sit amet
+
Lorem ipsum dolor sit amet
+
Lorem ipsum dolor sit amet
Lorem ipsum dolor sit amet
- Do something
), }; diff --git a/packages/pharos/src/components/sheet/pharos-sheet.scss b/packages/pharos/src/components/sheet/pharos-sheet.scss index 99d6b385d..15b0ac355 100644 --- a/packages/pharos/src/components/sheet/pharos-sheet.scss +++ b/packages/pharos/src/components/sheet/pharos-sheet.scss @@ -74,21 +74,13 @@ outline: 0; transition: height var(--pharos-transition-base); box-shadow: 0 1px 2px 0 rgb(18 18 18 / 0.3), 0 -4px 8px 3px rgb(18 18 18 / 0.15); - - .sheet__wrapper { - padding: 0 var(--pharos-spacing-one-and-a-half-x); - } - - @include mixins.until(small) { - .sheet__wrapper { - padding: 0 20px; - } - } + contain: layout; .sheet__handle { background: var(--pharos-color-marble-gray-80); width: var(--pharos-spacing-three-and-a-half-x); height: 4px; + min-height: 4px; border-radius: 2px; margin: var(--pharos-spacing-one-half-x) auto 0 auto; box-sizing: border-box; @@ -100,45 +92,25 @@ position: relative; flex: 1 1 auto; margin-top: 0.375rem; + overflow-y: auto; } .sheet__header { display: flex; align-items: flex-start; - justify-content: space-between; - margin-top: var(--pharos-spacing-one-and-a-half-x); -} - -.sheet__footer { - display: flex; - flex-wrap: wrap; - align-items: center; justify-content: flex-end; - padding: var(--pharos-spacing-1-x) var(--pharos-spacing-one-and-a-half-x); - - @at-root #{&}--empty { - padding: 0 0 var(--pharos-spacing-2-x); - } + margin-top: var(--pharos-spacing-one-and-a-half-x); + margin-right: var(--pharos-spacing-one-and-a-half-x); } @include mixins.until(small) { - .sheet__footer { - padding: var(--pharos-spacing-1-x) 1.25rem; + .sheet__header { + margin-right: 1.25rem; } } -slot[name='footer']::slotted(*) { - margin-left: var(--pharos-spacing-1-x); -} - :host([expanded]) { .sheet__content { height: 100%; } } - -:host([footer-divider]) { - .sheet__footer { - border-top: 1px solid var(--pharos-color-marble-gray-base); - } -} diff --git a/packages/pharos/src/components/sheet/pharos-sheet.test.ts b/packages/pharos/src/components/sheet/pharos-sheet.test.ts index 060fb0387..7539f1dbc 100644 --- a/packages/pharos/src/components/sheet/pharos-sheet.test.ts +++ b/packages/pharos/src/components/sheet/pharos-sheet.test.ts @@ -10,7 +10,9 @@ describe('pharos-sheet', () => { beforeEach(async () => { component = await fixture( - html` My Sheet ` + html` + My Sheet + ` ); }); diff --git a/packages/pharos/src/components/sheet/pharos-sheet.ts b/packages/pharos/src/components/sheet/pharos-sheet.ts index 0b45c7334..d49457d91 100644 --- a/packages/pharos/src/components/sheet/pharos-sheet.ts +++ b/packages/pharos/src/components/sheet/pharos-sheet.ts @@ -21,7 +21,6 @@ const FOCUS_ELEMENT = `[data-sheet-focus]`; * * @slot description - Content that describes the primary message or purpose of the sheet. * @slot - Contains the content of the sheet body. - * @slot footer - Contains the content of the sheet footer. * * @fires pharos-sheet-open - Fires when the sheet is about to open - cancelable * @fires pharos-sheet-opened - Fires when the sheet has opened @@ -61,8 +60,8 @@ export class PharosSheet extends ScopedRegistryMixin(PharosElement) { * Indicates if the sheet contains close button. * @attr hasClose */ - @property({ type: Boolean, reflect: true }) - public hasClose = true; + @property({ type: Boolean, reflect: true, attribute: 'has-close' }) + public hasClose = false; /** * Text content for the sheet header @@ -71,20 +70,11 @@ export class PharosSheet extends ScopedRegistryMixin(PharosElement) { @property({ type: String, reflect: true }) public header = 'Sheet header'; - /** - * Indicates if the sheet footer should contain a divider. - * @attr footer-divider - */ - @property({ type: Boolean, reflect: true, attribute: 'footer-divider' }) - public footerDivider = false; - private _currentTrigger: Element | null = null; private _triggers!: NodeListOf; @state() - private _isFooterEmpty = true; - private _startHeight = 0; private _startY = 0; private _isDragging = false; @@ -236,13 +226,15 @@ export class PharosSheet extends ScopedRegistryMixin(PharosElement) { } private _handleDragEnd(): void { - this._isDragging = false; - const sheetContent = this.shadowRoot?.querySelector(`.sheet__content`) as HTMLDivElement; - const sheetHeight = sheetContent.clientHeight; - if (sheetHeight > this._startHeight) { - sheetContent.style.height = '100%'; - } else { - sheetContent.style.height = '50%'; + if (this._isDragging) { + this._isDragging = false; + const sheetContent = this.shadowRoot?.querySelector(`.sheet__content`) as HTMLDivElement; + const sheetHeight = sheetContent.clientHeight; + if (sheetHeight > this._startHeight) { + sheetContent.style.height = '100%'; + } else { + sheetContent.style.height = '50%'; + } } } @@ -301,20 +293,17 @@ export class PharosSheet extends ScopedRegistryMixin(PharosElement) { return descriptionSlot.length ? 'description' : undefined; } - private _handleFooterSlotchange(e: Event) { - this._isFooterEmpty = - (e.target as HTMLSlotElement).assignedNodes({ flatten: true }).length === 0; - } - private _renderCloseButton(): TemplateResult | typeof nothing { return this.hasClose - ? html` ` + ? html`
+ +
` : nothing; } @@ -338,21 +327,10 @@ export class PharosSheet extends ScopedRegistryMixin(PharosElement) { >
-
- ${this._renderSheetHandle()} -
- - ${this.header} - - ${this._renderCloseButton()} -
-
- ${this.descriptionContent} - -
-
-
- + ${this._renderSheetHandle()} ${this._renderCloseButton()} +
+ ${this.descriptionContent} +
diff --git a/packages/pharos/src/components/sheet/pharos-sheet.wc.stories.jsx b/packages/pharos/src/components/sheet/pharos-sheet.wc.stories.jsx index 1da7e3712..1f76cf50a 100644 --- a/packages/pharos/src/components/sheet/pharos-sheet.wc.stories.jsx +++ b/packages/pharos/src/components/sheet/pharos-sheet.wc.stories.jsx @@ -27,16 +27,45 @@ export const Base = { `, }; -export const WithFooter = { +export const WithClose = { render: () => html`
Click Me - -
Lorem ipsum dolor sit amet
- Do something + +
Lorem ipsum dolor sit amet
+
+
+ `, +}; + +export const LongContent = { + render: () => + html` +
+ + Click Me + + +
Lorem ipsum dolor sit amet
+
Lorem ipsum dolor sit amet
+
Lorem ipsum dolor sit amet
+
Lorem ipsum dolor sit amet
+
Lorem ipsum dolor sit amet
+
Lorem ipsum dolor sit amet
+
Lorem ipsum dolor sit amet
+
Lorem ipsum dolor sit amet
+
Lorem ipsum dolor sit amet
+
Lorem ipsum dolor sit amet
+
Lorem ipsum dolor sit amet
+
Lorem ipsum dolor sit amet
+
Lorem ipsum dolor sit amet
+
Lorem ipsum dolor sit amet
+
Lorem ipsum dolor sit amet
+
Lorem ipsum dolor sit amet
+
Lorem ipsum dolor sit amet
`, From 37f727be23bfafb9a44b55be49209e1da9b8f53a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 18 Sep 2023 10:00:41 -0400 Subject: [PATCH 14/19] chore: version packages (#615) Co-authored-by: github-actions[bot] --- .changeset/fresh-parrots-judge.md | 5 ----- packages/pharos/CHANGELOG.md | 6 ++++++ packages/pharos/package.json | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) delete mode 100644 .changeset/fresh-parrots-judge.md diff --git a/.changeset/fresh-parrots-judge.md b/.changeset/fresh-parrots-judge.md deleted file mode 100644 index 369f31321..000000000 --- a/.changeset/fresh-parrots-judge.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@ithaka/pharos': patch ---- - -Add overflow to sheet diff --git a/packages/pharos/CHANGELOG.md b/packages/pharos/CHANGELOG.md index edc1bd17c..9d04362f7 100644 --- a/packages/pharos/CHANGELOG.md +++ b/packages/pharos/CHANGELOG.md @@ -1,5 +1,11 @@ # @ithaka/pharos +## 12.26.1 + +### Patch Changes + +- [#613](https://github.com/ithaka/pharos/pull/613) [`ef13fa1`](https://github.com/ithaka/pharos/commit/ef13fa18638f630f55716c60d81c9ba8d23c255d) Thanks [@jialin-he](https://github.com/jialin-he)! - Add overflow to sheet + ## 12.26.0 ### Minor Changes diff --git a/packages/pharos/package.json b/packages/pharos/package.json index 22340b582..2d50ea36f 100644 --- a/packages/pharos/package.json +++ b/packages/pharos/package.json @@ -3,7 +3,7 @@ "publishConfig": { "access": "public" }, - "version": "12.26.0", + "version": "12.26.1", "description": "Pharos web components for products and experiences", "files": [ "lib", From d0268264b08e88246e6d3ffc17a9a20a57fabca3 Mon Sep 17 00:00:00 2001 From: Dane Hillard Date: Tue, 19 Sep 2023 11:26:25 -0400 Subject: [PATCH 15/19] Remove `paint` value from contain properties (#614) * fix: remove `paint` value from contain properties Of all the `contain` property values, `paint` is the most noticeably problematic when overused because it can bite people by cropping off the content they intended to render within a component that has `paint` containment. For components that rely on composition at the consumer, this has caused issues more than a few times. * chore: add changeset --- .changeset/five-moles-add.md | 5 +++++ .../src/components/dropdown-menu/pharos-dropdown-menu.scss | 2 +- packages/pharos/src/components/footer/pharos-footer.scss | 2 +- .../src/components/input-group/pharos-input-group.scss | 2 +- packages/pharos/src/components/modal/pharos-modal.scss | 2 +- packages/pharos/src/components/select/pharos-select.scss | 2 +- packages/pharos/src/components/sheet/pharos-sheet.scss | 2 +- .../pharos/src/components/sidenav/pharos-sidenav-menu.scss | 2 +- .../src/components/sidenav/pharos-sidenav-section.scss | 2 +- packages/pharos/src/components/tabs/pharos-tab.scss | 2 +- 10 files changed, 14 insertions(+), 9 deletions(-) create mode 100644 .changeset/five-moles-add.md diff --git a/.changeset/five-moles-add.md b/.changeset/five-moles-add.md new file mode 100644 index 000000000..3e31245cb --- /dev/null +++ b/.changeset/five-moles-add.md @@ -0,0 +1,5 @@ +--- +'@ithaka/pharos': major +--- + +Remove `paint` value of contain property globally diff --git a/packages/pharos/src/components/dropdown-menu/pharos-dropdown-menu.scss b/packages/pharos/src/components/dropdown-menu/pharos-dropdown-menu.scss index e80133c44..35f0761a7 100644 --- a/packages/pharos/src/components/dropdown-menu/pharos-dropdown-menu.scss +++ b/packages/pharos/src/components/dropdown-menu/pharos-dropdown-menu.scss @@ -14,7 +14,7 @@ background-color: var(--pharos-color-ui-10); position: relative; - contain: paint; + contain: layout; margin: 0; list-style-type: none; padding-left: 0; diff --git a/packages/pharos/src/components/footer/pharos-footer.scss b/packages/pharos/src/components/footer/pharos-footer.scss index 2b49823ed..7bf41dbc4 100644 --- a/packages/pharos/src/components/footer/pharos-footer.scss +++ b/packages/pharos/src/components/footer/pharos-footer.scss @@ -2,7 +2,7 @@ :host { display: block; - contain: content; + contain: layout style; } #footer-element { diff --git a/packages/pharos/src/components/input-group/pharos-input-group.scss b/packages/pharos/src/components/input-group/pharos-input-group.scss index e9434deea..d2db912af 100644 --- a/packages/pharos/src/components/input-group/pharos-input-group.scss +++ b/packages/pharos/src/components/input-group/pharos-input-group.scss @@ -1,7 +1,7 @@ :host { display: block; width: 100%; - contain: paint; + contain: layout; } .input-group { diff --git a/packages/pharos/src/components/modal/pharos-modal.scss b/packages/pharos/src/components/modal/pharos-modal.scss index 4151a6010..07ebde08f 100644 --- a/packages/pharos/src/components/modal/pharos-modal.scss +++ b/packages/pharos/src/components/modal/pharos-modal.scss @@ -6,7 +6,7 @@ z-index: 9000; pointer-events: none; inset: 0; - contain: strict; + contain: layout size style; } .modal__overlay { diff --git a/packages/pharos/src/components/select/pharos-select.scss b/packages/pharos/src/components/select/pharos-select.scss index 0778345d5..e855d738d 100644 --- a/packages/pharos/src/components/select/pharos-select.scss +++ b/packages/pharos/src/components/select/pharos-select.scss @@ -3,7 +3,7 @@ :host { display: block; width: 100%; - contain: paint; + contain: layout; } #select-element { diff --git a/packages/pharos/src/components/sheet/pharos-sheet.scss b/packages/pharos/src/components/sheet/pharos-sheet.scss index 99d6b385d..4013c6e53 100644 --- a/packages/pharos/src/components/sheet/pharos-sheet.scss +++ b/packages/pharos/src/components/sheet/pharos-sheet.scss @@ -6,7 +6,7 @@ z-index: 9000; pointer-events: none; inset: 0; - contain: strict; + contain: layout size style; } .sheet__overlay { diff --git a/packages/pharos/src/components/sidenav/pharos-sidenav-menu.scss b/packages/pharos/src/components/sidenav/pharos-sidenav-menu.scss index 351ea30be..8e2e3ff6c 100644 --- a/packages/pharos/src/components/sidenav/pharos-sidenav-menu.scss +++ b/packages/pharos/src/components/sidenav/pharos-sidenav-menu.scss @@ -2,7 +2,7 @@ :host { display: block; - contain: content; + contain: layout style; } #button-element { diff --git a/packages/pharos/src/components/sidenav/pharos-sidenav-section.scss b/packages/pharos/src/components/sidenav/pharos-sidenav-section.scss index 4df4cb59d..abfcb321a 100644 --- a/packages/pharos/src/components/sidenav/pharos-sidenav-section.scss +++ b/packages/pharos/src/components/sidenav/pharos-sidenav-section.scss @@ -1,6 +1,6 @@ :host { display: block; - contain: content; + contain: layout style; } .section__container { diff --git a/packages/pharos/src/components/tabs/pharos-tab.scss b/packages/pharos/src/components/tabs/pharos-tab.scss index 5dc77c021..5dc7f29f5 100644 --- a/packages/pharos/src/components/tabs/pharos-tab.scss +++ b/packages/pharos/src/components/tabs/pharos-tab.scss @@ -9,7 +9,7 @@ border: none; background: none; border-bottom: 1px solid var(--pharos-color-ui-40); - contain: content; + contain: layout style; display: grid; grid-template-columns: max-content; From 6ec2d695f4fcaefc46616f7e582d9e71f641a4f6 Mon Sep 17 00:00:00 2001 From: Jialin He <38861633+jialin-he@users.noreply.github.com> Date: Mon, 25 Sep 2023 10:47:37 -0400 Subject: [PATCH 16/19] Sheet: Update default height and emit event for expand and collapse (#617) * feat(sheet): fix sheet height when click * feat(sheet): emit event for expand and collapse * feat(sheet): add changeset * feat(sheet): make enable expansion an attribute --- .changeset/thick-bugs-sing.md | 5 +++++ .../pharos/src/components/sheet/pharos-sheet.scss | 2 +- packages/pharos/src/components/sheet/pharos-sheet.ts | 11 +++++++++-- 3 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 .changeset/thick-bugs-sing.md diff --git a/.changeset/thick-bugs-sing.md b/.changeset/thick-bugs-sing.md new file mode 100644 index 000000000..c4612e021 --- /dev/null +++ b/.changeset/thick-bugs-sing.md @@ -0,0 +1,5 @@ +--- +'@ithaka/pharos': patch +--- + +Update default height to sheet and emit events diff --git a/packages/pharos/src/components/sheet/pharos-sheet.scss b/packages/pharos/src/components/sheet/pharos-sheet.scss index 15b0ac355..836ade90f 100644 --- a/packages/pharos/src/components/sheet/pharos-sheet.scss +++ b/packages/pharos/src/components/sheet/pharos-sheet.scss @@ -63,7 +63,7 @@ display: flex; flex-direction: column; justify-content: space-between; - height: 50%; + height: 60%; width: 100%; pointer-events: auto; overflow-y: scroll; diff --git a/packages/pharos/src/components/sheet/pharos-sheet.ts b/packages/pharos/src/components/sheet/pharos-sheet.ts index d49457d91..e9728ea24 100644 --- a/packages/pharos/src/components/sheet/pharos-sheet.ts +++ b/packages/pharos/src/components/sheet/pharos-sheet.ts @@ -53,7 +53,7 @@ export class PharosSheet extends ScopedRegistryMixin(PharosElement) { * Indicates if the sheet is allowed to expand. * @attr enableExpansion */ - @property({ type: Boolean, reflect: true }) + @property({ type: Boolean, reflect: true, attribute: 'enable-expansion' }) public enableExpansion = true; /** @@ -230,10 +230,17 @@ export class PharosSheet extends ScopedRegistryMixin(PharosElement) { this._isDragging = false; const sheetContent = this.shadowRoot?.querySelector(`.sheet__content`) as HTMLDivElement; const sheetHeight = sheetContent.clientHeight; + const details = { + bubbles: true, + composed: true, + }; + if (sheetHeight === this._startHeight) return; if (sheetHeight > this._startHeight) { sheetContent.style.height = '100%'; + this.dispatchEvent(new CustomEvent('pharos-sheet-expanded', details)); } else { - sheetContent.style.height = '50%'; + sheetContent.style.height = '60%'; + this.dispatchEvent(new CustomEvent('pharos-sheet-collapsed', details)); } } } From dca77bc25d13bf2faebbe8abda1de346b69b7ac6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 25 Sep 2023 10:54:19 -0400 Subject: [PATCH 17/19] chore: version packages (#619) Co-authored-by: github-actions[bot] --- .changeset/thick-bugs-sing.md | 5 ----- packages/pharos/CHANGELOG.md | 6 ++++++ packages/pharos/package.json | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) delete mode 100644 .changeset/thick-bugs-sing.md diff --git a/.changeset/thick-bugs-sing.md b/.changeset/thick-bugs-sing.md deleted file mode 100644 index c4612e021..000000000 --- a/.changeset/thick-bugs-sing.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@ithaka/pharos': patch ---- - -Update default height to sheet and emit events diff --git a/packages/pharos/CHANGELOG.md b/packages/pharos/CHANGELOG.md index 9d04362f7..d98a1e9c1 100644 --- a/packages/pharos/CHANGELOG.md +++ b/packages/pharos/CHANGELOG.md @@ -1,5 +1,11 @@ # @ithaka/pharos +## 12.26.2 + +### Patch Changes + +- [#617](https://github.com/ithaka/pharos/pull/617) [`6ec2d69`](https://github.com/ithaka/pharos/commit/6ec2d695f4fcaefc46616f7e582d9e71f641a4f6) Thanks [@jialin-he](https://github.com/jialin-he)! - Update default height to sheet and emit events + ## 12.26.1 ### Patch Changes diff --git a/packages/pharos/package.json b/packages/pharos/package.json index 2d50ea36f..1d3996d62 100644 --- a/packages/pharos/package.json +++ b/packages/pharos/package.json @@ -3,7 +3,7 @@ "publishConfig": { "access": "public" }, - "version": "12.26.1", + "version": "12.26.2", "description": "Pharos web components for products and experiences", "files": [ "lib", From d67125a4a9598475aff9000b18567f734074915f Mon Sep 17 00:00:00 2001 From: Henry Long <52258386+henryclong@users.noreply.github.com> Date: Fri, 29 Sep 2023 14:45:09 -0400 Subject: [PATCH 18/19] Coach Mark: Component contribution (#608) * feat: initialize coach mark component boilerplate * feat: register coach mark to ensure that it is usable within storybook examples * style: add barebones coach mark styling * feat: build out basic coach mark structure using pharos components * feat: hide/show coach mark based on attribute * style: style coach mark pip * feat: add example div for storybook testing * feat: add attributes to define sides and alignment * feat: position coach mark around target element based on user selection * feat: reflect pip alignment in coach mark styling * feat: allow header and content to be set dynamically * feat: setup storybook configurations * style: animate fade in * feat: animate coach mark fade in and add optional delay * feat: define light and dark variants for coach mark * feat: allow setting of minumum width * style: adjust pip position * fix: fix build issue that occured during react component generation * feat: reposition coach mark when window is resized * feat: add coach mark unit tests * style: fix dark mode close button color * feat: add coach mark aria attributes * fix: make coach mark two words * feat: add changeset * docs: add @henryclong as a contributor * fix: update comments, remove check for body element observer * feat: add story to coach mark react page * docs: add coach mark page to pharos documentation * fix: change default coach mark appearance to dark * fix: allow body text to extend to right edge of coach mark * fix: fix bug that prevented min-width from being properly set by width property * fix: ensure that coach mark is registered with pharos-site package * feat: utilize floating-dom to calculate coach mark position, remove manual calculations * style: remove unnecessary styling, reduce styling nesting introduction of floating-ui allowed for removal of certain styles needed for content positioning * fix: ensure that coach mark position is recalculated on scroll * fix: use autoUpdate instead of resize observer, remove requestUpdate from render method * fix: reduce nesting by giving heading an appropriate class, use variable for padding value * fix: set default width to 30ch, convert width to string to allow for different units to be used * fix: correct react storybook coach mark page name * fix: define control for coach mark header input * fix: ensure that pip always points towards target element, regardless of alignment * fix: move pharos token values into tokens/components/coach-mark.json * fix: prepend computedSide property name with underscore * fix: bring side denoting class in line with bem comventions * fix: simplify pip border math, correct border size variable value and reference typo * fix: add fallback values for pharos tokens used in coach mark styles * fix: ensure that fallback min-width is in line with component default min-width --- .all-contributorsrc | 9 + .changeset/sour-knives-poke.md | 5 + .storybook/initComponents.js | 2 + README.md | 1 + packages/pharos-site/initComponents.tsx | 1 + .../src/pages/components/coach-mark/index.mdx | 3 + .../static/guidelines/coach-mark.docs.mdx | 153 ++++++++++++++ .../PharosCoachMark.react.stories.jsx | 59 ++++++ .../coach-mark/pharos-coach-mark.scss | 190 ++++++++++++++++++ .../coach-mark/pharos-coach-mark.test.ts | 75 +++++++ .../coach-mark/pharos-coach-mark.ts | 149 ++++++++++++++ .../pharos-coach-mark.wc.stories.jsx | 40 ++++ .../src/components/coach-mark/storyArgs.ts | 51 +++++ packages/pharos/src/index.ts | 1 + packages/pharos/src/test/initComponents.ts | 2 + .../pharos/tokens/components/coach-mark.json | 27 +++ 16 files changed, 768 insertions(+) create mode 100644 .changeset/sour-knives-poke.md create mode 100644 packages/pharos-site/src/pages/components/coach-mark/index.mdx create mode 100644 packages/pharos-site/static/guidelines/coach-mark.docs.mdx create mode 100644 packages/pharos/src/components/coach-mark/PharosCoachMark.react.stories.jsx create mode 100644 packages/pharos/src/components/coach-mark/pharos-coach-mark.scss create mode 100644 packages/pharos/src/components/coach-mark/pharos-coach-mark.test.ts create mode 100644 packages/pharos/src/components/coach-mark/pharos-coach-mark.ts create mode 100644 packages/pharos/src/components/coach-mark/pharos-coach-mark.wc.stories.jsx create mode 100644 packages/pharos/src/components/coach-mark/storyArgs.ts create mode 100644 packages/pharos/tokens/components/coach-mark.json diff --git a/.all-contributorsrc b/.all-contributorsrc index 5098cd451..86b61b399 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -309,6 +309,15 @@ "code" ] }, + { + "login": "henryclong", + "name": "Henry Long", + "avatar_url": "https://avatars.githubusercontent.com/u/52258386?v=4", + "profile": "https://github.com/henryclong", + "contributions": [ + "code" + ] + }, { "login": "sirrah-tam", "name": "Mat Harris", diff --git a/.changeset/sour-knives-poke.md b/.changeset/sour-knives-poke.md new file mode 100644 index 000000000..1d6395c82 --- /dev/null +++ b/.changeset/sour-knives-poke.md @@ -0,0 +1,5 @@ +--- +'@ithaka/pharos': major +--- + +Adds the coach mark component, which is used to highlight new or unique features diff --git a/.storybook/initComponents.js b/.storybook/initComponents.js index 6ec0295bb..7a4e59708 100644 --- a/.storybook/initComponents.js +++ b/.storybook/initComponents.js @@ -5,6 +5,7 @@ import { PharosButton, PharosCheckbox, PharosCheckboxGroup, + PharosCoachMark, PharosCombobox, PharosDropdownMenu, PharosDropdownMenuItem, @@ -54,6 +55,7 @@ registerComponents('storybook', [ PharosButton, PharosCheckbox, PharosCheckboxGroup, + PharosCoachMark, PharosCombobox, PharosDropdownMenu, PharosDropdownMenuItem, diff --git a/README.md b/README.md index 40809e835..621e2a1e6 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d Hassan Tanveer
Hassan Tanveer

💻 mariadevadoss
mariadevadoss

💻 Brent Swisher
Brent Swisher

🐛 💻 + Henry Long
Henry Long

💻 Mat Harris
Mat Harris

️️️️♿️ diff --git a/packages/pharos-site/initComponents.tsx b/packages/pharos-site/initComponents.tsx index f03ea2bb1..15fefdd22 100644 --- a/packages/pharos-site/initComponents.tsx +++ b/packages/pharos-site/initComponents.tsx @@ -10,6 +10,7 @@ if (typeof window !== `undefined`) { pharos.PharosButton, pharos.PharosCheckbox, pharos.PharosCheckboxGroup, + pharos.PharosCoachMark, pharos.PharosCombobox, pharos.PharosDropdownMenu, pharos.PharosDropdownMenuItem, diff --git a/packages/pharos-site/src/pages/components/coach-mark/index.mdx b/packages/pharos-site/src/pages/components/coach-mark/index.mdx new file mode 100644 index 000000000..62724a3e3 --- /dev/null +++ b/packages/pharos-site/src/pages/components/coach-mark/index.mdx @@ -0,0 +1,3 @@ +import Intro from '@guidelines/coach-mark.docs'; + + diff --git a/packages/pharos-site/static/guidelines/coach-mark.docs.mdx b/packages/pharos-site/static/guidelines/coach-mark.docs.mdx new file mode 100644 index 000000000..d25708281 --- /dev/null +++ b/packages/pharos-site/static/guidelines/coach-mark.docs.mdx @@ -0,0 +1,153 @@ +import PageSection from '@components/statics/PageSection.tsx'; +import BestPractices from '@components/statics/BestPractices.tsx'; + + + +## Examples + +```jsx live +<> +
+ Lorem Ipsum +
+ + This is an example Coach Mark + + +``` + + + +
    +
  • When a user visits a page for the first time
  • +
  • When a new feature is added
  • +
  • Inform a user after they activated a process
  • +
+
+ +

+ There are 12 placement options available. Coach marks can be placed top, bottom, left or right + of the target (e.g. button, icon, link, etc.) and the caret (triangle) can be placed at the + start, center or end of the coach mark. +

+

+ Where should I place a coach mark? +

+

+ The coach mark should be in close proximity to the feature that it highlights. There are 2 + preferred placement options out of a total of the available 12. A coachmark should be placed + either on the top or bottom of a feature with the caret (triangle) appearing in the center. + The other placement options should only be used so that important features or information is + not hidden. +

+
+ +
    +
  1. + Informational: Focuses the attention of the user on a section of the page or process taking + place. +
  2. +
  3. Instructional: Highlights a feature or action.
  4. +
+
+ +

+ The dark coach mark variant is the default state. Most of the backgrounds on JSTOR are white + and should use the dark coach mark for contrast. Consider using the light coach mark variant + when the coach mark will appear over dark backgrounds. +

+
+
+ + +
  • + Use coach marks to focus attention to important functionality and feedback in context +
  • +
  • Display coach marks on a page once per session
  • +
  • Checkboxes should always include a label
  • +
  • + Consider a coach mark to promote new features, functionality, or other changes to layout + and patterns that may disorient the user +
  • + + } + Dont={ +
      +
    • + Do not repetitively display the same coach mark. You should be able to track whether the + user has dismissed the coach mark and be confident it will not reappear +
    • +
    • + Do not overuse the coach mark and consider where else in the user's journey they may be + interacting with coach marks within the product +
    • +
    • + Do not add more than one coach mark to a page. If features need explanation consider a + tool tip or other passive communication devices (badges, external links, etc) +
    • +
    + } + /> +
    + +

    + The coach mark headline and body copy should be informative, actionable, and concise. It should + Include the minimum amount of information that users need to know. It should not repeat what is + already visibly available to the user in the interface. For example, you should not use + "Workspace" as a headline if the coach mark is targeting a "Workspace" navigation link. Rather + you would expect something actionable like.. "Find what you saved". Content should be written in + sentence case. Refer to{' '} + + Voice and Tone guidelines + {' '} + for more guidance. +

    +
    + + +
      +
    • + + 4.1.3 Status Messages + +
    • +
    • + + 1.3.1 Info and Relationships A + +
    • +
    +
    + +
      +
    • Button contains "aria-haspopup="dialog"
    • +
    +
    +
    diff --git a/packages/pharos/src/components/coach-mark/PharosCoachMark.react.stories.jsx b/packages/pharos/src/components/coach-mark/PharosCoachMark.react.stories.jsx new file mode 100644 index 000000000..aeebb71c2 --- /dev/null +++ b/packages/pharos/src/components/coach-mark/PharosCoachMark.react.stories.jsx @@ -0,0 +1,59 @@ +import { PharosCoachMark } from '../../react-components/coach-mark/pharos-coach-mark'; +import { configureDocsPage } from '@config/docsPageConfig'; +import { PharosContext } from '../../utils/PharosContext'; + +export default { + title: 'Components/Coach Mark', + component: PharosCoachMark, + decorators: [ + (Story) => ( + + + + ), + ], + parameters: { + docs: { + page: configureDocsPage('coach-mark'), + }, + }, +}; + +export const Base = { + render: (args) => ( + <> +
    + Lorem Ipsum +
    + + This is an example Coach Mark + + + ), + args: { + hide: false, + side: 'bottom', + alignment: 'start', + header: 'Coach Mark', + delay: 'short', + variant: 'dark', + width: '30ch', + }, +}; diff --git a/packages/pharos/src/components/coach-mark/pharos-coach-mark.scss b/packages/pharos/src/components/coach-mark/pharos-coach-mark.scss new file mode 100644 index 000000000..40d11a6df --- /dev/null +++ b/packages/pharos/src/components/coach-mark/pharos-coach-mark.scss @@ -0,0 +1,190 @@ +@keyframes fadein { + 0% { + opacity: 0; + } + + 100% { + opacity: 1; + } +} + +@keyframes fadein-short { + 0% { + opacity: 0; + } + + 37.5% { + opacity: 0; + } + + 100% { + opacity: 1; + } +} + +@keyframes fadein-long { + 0% { + opacity: 0; + } + + 66.66% { + opacity: 0; + } + + 100% { + opacity: 1; + } +} + +:host { + --coach-mark-background-color: var( + --pharos-coach-mark-color-background-light, + --pharos-color-white + ); + --coach-mark-border-color: var( + --pharos-coach-mark-color-border-light, + --pharos-color-marble-gray-base + ); + --coach-mark-heading-color: var( + --pharos-coach-mark-color-heading-light, + --pharos-color-marble-gray-20 + ); + --coach-mark-text-color: var(--pharos-coach-mark-color-text-light, --pharos-color-marble-gray-20); + + display: block; + position: absolute; +} + +:host([variant='dark']) { + --coach-mark-background-color: var( + --pharos-coach-mark-color-background-dark, + --pharos-button-color-base-overlay-background-base + ); + --coach-mark-border-color: var( + --pharos-coach-mark-color-border-dark, + --coach-mark-background-color + ); + --coach-mark-heading-color: var(--pharos-coach-mark-color-heading-dark, --pharos-color-white); + --coach-mark-text-color: var(--pharos-coach-mark-color-text-dark, --pharos-color-marble-gray-80); +} + +.coach-mark { + animation-duration: 1000ms; /* Delay duration of 0ms, fade duration of --pharos-transition-duration-default */ + animation-name: fadein; + animation-timing-function: ease-in-out; + + &[aria-hidden='true'] { + display: none; + opacity: 0; + } + + &.delay-short { + animation-duration: 1600ms; /* Delay duration of 600ms, fade duration of --pharos-transition-duration-default */ + animation-name: fadein-short; + } + + &.delay-long { + animation-duration: 3000ms; /* Delay duration of 2000ms, fade duration of --pharos-transition-duration-default */ + animation-name: fadein-long; + } +} + +.coach-mark__content { + position: relative; + background-color: var(--coach-mark-background-color); + border: var(--pharos-coach-mark-size-border, 1px) solid var(--coach-mark-border-color); + padding: var(--pharos-spacing-one-half-x) var(--pharos-spacing-three-quarters-x); + min-width: 30ch; + width: fit-content; + font-size: var(--pharos-font-size-small); + color: var(--coach-mark-text-color); + line-height: var(--pharos-spacing-1-x); + + &::before, + &::after { + content: ''; + background-color: var(--coach-mark-background-color); + position: absolute; + top: var(--pip-top); + left: var(--pip-left); + height: var(--pharos-coach-mark-size-pip, 10px); + width: var(--pharos-coach-mark-size-pip, 10px); + transform: translate(-50%, -50%) rotate(45deg); + z-index: 1; + } + + &::after { + border: var(--pharos-coach-mark-size-border, 1px) solid var(--coach-mark-border-color); + z-index: -1; + } +} + +.coach-mark__heading { + color: var(--coach-mark-heading-color); + padding-right: var(--pharos-spacing-one-and-a-half-x); +} + +.coach-mark__close { + position: absolute; + top: 0; + right: 0; + z-index: 1; +} + +.coach-mark__wrapper { + height: fit-content; + width: fit-content; +} + +.coach-mark-side--right .coach-mark__content { + --pip-left: 0; +} + +.coach-mark-side--left .coach-mark__content { + --pip-left: 100%; +} + +.coach-mark-side--top .coach-mark__content { + --pip-top: 100%; +} + +.coach-mark-side--bottom .coach-mark__content { + --pip-top: 0; +} + +.coach-mark-side--right, +.coach-mark-side--left { + .coach-mark-alignment__start { + --pip-top: 15%; + } + + .coach-mark-alignment__center { + --pip-top: 50%; + } + + .coach-mark-alignment__end { + --pip-top: 85%; + } +} + +.coach-mark-side--top, +.coach-mark-side--bottom { + .coach-mark-alignment__start { + --pip-left: 15%; + } + + .coach-mark-alignment__center { + --pip-left: 50%; + } + + .coach-mark-alignment__end { + --pip-left: 85%; + } +} + +.coachmark-example-div { + background-color: var(--pharos-color-white); + border: 1px solid var(--pharos-color-marble-gray-base); + padding: var(--pharos-spacing-one-half-x) var(--pharos-spacing-three-quarters-x); + width: fit-content; +} diff --git a/packages/pharos/src/components/coach-mark/pharos-coach-mark.test.ts b/packages/pharos/src/components/coach-mark/pharos-coach-mark.test.ts new file mode 100644 index 000000000..ffca8c56a --- /dev/null +++ b/packages/pharos/src/components/coach-mark/pharos-coach-mark.test.ts @@ -0,0 +1,75 @@ +import { fixture, expect } from '@open-wc/testing'; +import { html } from 'lit/static-html.js'; +import sinon from 'sinon'; +import type { SinonSpy } from 'sinon'; +import type { PharosCoachMark } from './pharos-coach-mark'; +import type { PharosButton } from '../button/pharos-button'; + +describe('pharos-coach-mark', () => { + let component: PharosCoachMark, logSpy: SinonSpy; + + beforeEach(async () => { + component = await fixture( + html`Test Description` + ); + }); + + before(() => { + logSpy = sinon.spy(console, 'error'); + }); + + after(() => { + logSpy.restore(); + }); + + it('is accessible', async () => { + expect(component).to.be.accessible(); + }); + + it('is accessible when opened', async () => { + component.hide = false; + await component.updateComplete; + expect(component).to.be.accessible(); + }); + + it('has an attribute to open the coach mark', async () => { + component.hide = false; + await component.updateComplete; + expect(component.hide).to.be.false; + }); + + it('has an attribute to close the coach mark', async () => { + component.hide = false; + await component.updateComplete; + + component.hide = true; + await component.updateComplete; + expect(component.hide).to.be.true; + }); + + it('closes when the close button is clicked', async () => { + component.hide = false; + await component.updateComplete; + + const closeButton = component.renderRoot.querySelector('#close-button') as PharosButton; + closeButton.click(); + await component.updateComplete; + + expect(component.hide).to.be.true; + }); + + it('displays the header set in the element attribute', async () => { + component = await fixture( + html`Test Description` + ); + const header = component.renderRoot.querySelector('pharos-heading'); + expect(header).to.have.text('Test Header'); + }); + + it('displays content added as a child to the element', async () => { + component = await fixture( + html`Test Description` + ); + expect(component).to.have.text('Test Description'); + }); +}); diff --git a/packages/pharos/src/components/coach-mark/pharos-coach-mark.ts b/packages/pharos/src/components/coach-mark/pharos-coach-mark.ts new file mode 100644 index 000000000..7912a38fc --- /dev/null +++ b/packages/pharos/src/components/coach-mark/pharos-coach-mark.ts @@ -0,0 +1,149 @@ +import { PharosElement } from '../base/pharos-element'; +import { PharosButton } from '../button/pharos-button'; +import { PharosHeading } from '../heading/pharos-heading'; +import { html } from 'lit'; +import { property } from 'lit/decorators.js'; +import type { TemplateResult, CSSResultArray } from 'lit'; +import { coachMarkStyles } from './pharos-coach-mark.css'; +import ScopedRegistryMixin from '../../utils/mixins/scoped-registry'; +import { autoUpdate, computePosition, flip, offset } from '@floating-ui/dom'; + +export type Side = 'top' | 'right' | 'bottom' | 'left'; +export type Alignment = 'start' | 'center' | 'end'; +export type Delay = 'none' | 'short' | 'long'; +export type Variant = 'light' | 'dark'; + +/** + * Pharos coach mark component. + * + * @tag pharos-coach-mark + * + */ +export class PharosCoachMark extends ScopedRegistryMixin(PharosElement) { + static elementDefinitions = { + 'pharos-button': PharosButton, + 'pharos-heading': PharosHeading, + }; + + public static override get styles(): CSSResultArray { + return [coachMarkStyles]; + } + + /** + * Indicates that the coach mark should not be displayed on the page + * @attr hide + */ + @property({ type: Boolean, reflect: true }) + public hide = true; + + /** + * Indicates which side of the base element the coachmark should appear on + * @attr side + * @type {Side} + */ + @property({ reflect: true }) + public side: Side = 'bottom'; + + /** + * Indicates how the coach mark carat should be aligned in relation to the coach mark content + * @attr alignment + * @type {Alignment} + */ + @property({ reflect: true }) + public alignment: Alignment = 'center'; + + /** + * Dictates how long to wait between coach mark trigger and start of coach mark fade in animation + * @attr delay + * @type {Delay} + */ + @property({ reflect: true }) + public delay: Delay = 'short'; + + /** + * Text content for the coach mark header + * @attr header + * @type {String} + */ + @property({ reflect: true }) + public header = ''; + + /** + * Style variant + * @attr variant + * @type {Variant} + */ + @property({ reflect: true }) + public variant: Variant = 'dark'; + + /** + * Set minimum width of coach mark + * @attr width + * @type {string} + */ + @property({ reflect: true }) + public width = '30ch'; + + private _computedSide = 'bottom'; + + override connectedCallback() { + super.connectedCallback(); + this.setOffset(); + } + + private setOffset() { + const id: string = this.getAttribute('id') || ''; + const targetElement: Element | null = document.querySelector(`[data-coach-mark="${id}"]`); + if (!targetElement) return; + + this._cleanup = autoUpdate(targetElement, this, () => + computePosition(targetElement, this, { + placement: this.alignment === 'center' ? this.side : `${this.side}-${this.alignment}`, + middleware: [flip(), offset(20)], + }).then(({ x, y, placement }) => { + Object.assign(this.style, { + left: `${x}px`, + top: `${y}px`, + }); + this._computedSide = placement.toString().split('-')[0]; // Removes -start or -end from final placement + this.requestUpdate(); + }) + ); + } + + protected override render(): TemplateResult { + return html` + + `; + } +} diff --git a/packages/pharos/src/components/coach-mark/pharos-coach-mark.wc.stories.jsx b/packages/pharos/src/components/coach-mark/pharos-coach-mark.wc.stories.jsx new file mode 100644 index 000000000..5c5075fd9 --- /dev/null +++ b/packages/pharos/src/components/coach-mark/pharos-coach-mark.wc.stories.jsx @@ -0,0 +1,40 @@ +import { html } from 'lit'; +import { ifDefined } from 'lit/directives/if-defined.js'; + +import { argTypes, defaultArgs } from './storyArgs'; +import { configureDocsPage } from '@config/docsPageConfig'; + +export default { + title: 'Components/Coach Mark', + component: 'pharos-coach-mark', + parameters: { + docs: { + page: configureDocsPage('coach-mark'), + }, + options: { selectedPanel: 'addon-controls' }, + }, + argTypes, +}; + +export const Base = { + render: (args) => + html`
    + Lorem Ipsum +
    + + This is an example Coach Mark + `, + args: defaultArgs, +}; diff --git a/packages/pharos/src/components/coach-mark/storyArgs.ts b/packages/pharos/src/components/coach-mark/storyArgs.ts new file mode 100644 index 000000000..98d195314 --- /dev/null +++ b/packages/pharos/src/components/coach-mark/storyArgs.ts @@ -0,0 +1,51 @@ +export const defaultArgs = { + hide: false, + side: 'bottom', + alignment: 'start', + header: 'Coach Mark', + delay: 'short', + variant: 'dark', + width: '30ch', +}; + +export const argTypes = { + hide: { + control: { + type: 'boolean', + }, + }, + header: { + control: { + type: 'text', + }, + }, + side: { + options: ['top', 'right', 'bottom', 'left'], + control: { + type: 'inline-radio', + }, + }, + alignment: { + options: ['start', 'center', 'end'], + control: { + type: 'inline-radio', + }, + }, + delay: { + options: ['none', 'short', 'long'], + control: { + type: 'inline-radio', + }, + }, + variant: { + options: ['light', 'dark'], + control: { + type: 'inline-radio', + }, + }, + width: { + control: { + type: 'text', + }, + }, +}; diff --git a/packages/pharos/src/index.ts b/packages/pharos/src/index.ts index 5b19b30c7..9d91f7ccf 100644 --- a/packages/pharos/src/index.ts +++ b/packages/pharos/src/index.ts @@ -41,5 +41,6 @@ export { PharosLayout } from './components/layout/pharos-layout'; export { PharosImageCard } from './components/image-card/pharos-image-card'; export { PharosToggleButton } from './components/toggle-button-group/pharos-toggle-button'; export { PharosToggleButtonGroup } from './components/toggle-button-group/pharos-toggle-button-group'; +export { PharosCoachMark } from './components/coach-mark/pharos-coach-mark'; export { PharosPopover } from './components/popover/pharos-popover'; export { PharosSheet } from './components/sheet/pharos-sheet'; diff --git a/packages/pharos/src/test/initComponents.ts b/packages/pharos/src/test/initComponents.ts index 209a1660d..55a1c98c2 100644 --- a/packages/pharos/src/test/initComponents.ts +++ b/packages/pharos/src/test/initComponents.ts @@ -5,6 +5,7 @@ import { PharosButton, PharosCheckbox, PharosCheckboxGroup, + PharosCoachMark, PharosCombobox, PharosDropdownMenu, PharosDropdownMenuItem, @@ -54,6 +55,7 @@ registerComponents('test', [ PharosButton, PharosCheckbox, PharosCheckboxGroup, + PharosCoachMark, PharosCombobox, PharosDropdownMenu, PharosDropdownMenuItem, diff --git a/packages/pharos/tokens/components/coach-mark.json b/packages/pharos/tokens/components/coach-mark.json new file mode 100644 index 000000000..05196cf8a --- /dev/null +++ b/packages/pharos/tokens/components/coach-mark.json @@ -0,0 +1,27 @@ +{ + "coach-mark": { + "color": { + "background": { + "dark" : { "value": "{color.Marble gray.10.value}" }, + "light" : { "value": "{color.White.value}" } + }, + "border": { + "dark" : { "value": "{color.Marble gray.10.value}" }, + "light" : { "value": "{color.Marble gray.base.value}" } + }, + "heading": { + "dark" : { "value": "{color.White.value}" }, + "light" : { "value": "{color.Marble gray.20.value}" } + }, + "text": { + "dark" : { "value": "{color.Marble gray.80.value}" }, + "light" : { "value": "{color.Marble gray.20.value}" } + } + }, + "size": { + "text": { "value": "{font.size.small.value}"}, + "border": { "value": "1px"}, + "pip": { "value": "10px"} + } + } +} From 43069ef5d3c3a44d6dbad6789d8aa05997ee612b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 29 Sep 2023 14:50:17 -0400 Subject: [PATCH 19/19] chore: version packages (#622) Co-authored-by: github-actions[bot] --- .changeset/sour-knives-poke.md | 5 ----- packages/pharos-site/CHANGELOG.md | 7 +++++++ packages/pharos-site/package.json | 4 ++-- packages/pharos/CHANGELOG.md | 6 ++++++ packages/pharos/package.json | 2 +- 5 files changed, 16 insertions(+), 8 deletions(-) delete mode 100644 .changeset/sour-knives-poke.md diff --git a/.changeset/sour-knives-poke.md b/.changeset/sour-knives-poke.md deleted file mode 100644 index 1d6395c82..000000000 --- a/.changeset/sour-knives-poke.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@ithaka/pharos': major ---- - -Adds the coach mark component, which is used to highlight new or unique features diff --git a/packages/pharos-site/CHANGELOG.md b/packages/pharos-site/CHANGELOG.md index fa6899999..8f6840e2c 100644 --- a/packages/pharos-site/CHANGELOG.md +++ b/packages/pharos-site/CHANGELOG.md @@ -1,5 +1,12 @@ # @ithaka/pharos-site +## 5.13.4 + +### Patch Changes + +- Updated dependencies [[`d67125a`](https://github.com/ithaka/pharos/commit/d67125a4a9598475aff9000b18567f734074915f)]: + - @ithaka/pharos@13.0.0 + ## 5.13.3 ### Patch Changes diff --git a/packages/pharos-site/package.json b/packages/pharos-site/package.json index b3d2aed52..949c84b90 100644 --- a/packages/pharos-site/package.json +++ b/packages/pharos-site/package.json @@ -4,9 +4,9 @@ "access": "public" }, "description": "Pharos acts as a guiding light to helps us create consistent, dependable and focused experiences for all of JSTOR users.", - "version": "5.13.3", + "version": "5.13.4", "dependencies": { - "@ithaka/pharos": "^12.20.0", + "@ithaka/pharos": "^13.0.0", "@mdx-js/mdx": "^1.6.22", "@mdx-js/react": "^1.6.22", "@webcomponents/scoped-custom-element-registry": "^0.0.3", diff --git a/packages/pharos/CHANGELOG.md b/packages/pharos/CHANGELOG.md index d98a1e9c1..210941865 100644 --- a/packages/pharos/CHANGELOG.md +++ b/packages/pharos/CHANGELOG.md @@ -1,5 +1,11 @@ # @ithaka/pharos +## 13.0.0 + +### Major Changes + +- [#608](https://github.com/ithaka/pharos/pull/608) [`d67125a`](https://github.com/ithaka/pharos/commit/d67125a4a9598475aff9000b18567f734074915f) Thanks [@henryclong](https://github.com/henryclong)! - Adds the coach mark component, which is used to highlight new or unique features + ## 12.26.2 ### Patch Changes diff --git a/packages/pharos/package.json b/packages/pharos/package.json index 1d3996d62..e236415e3 100644 --- a/packages/pharos/package.json +++ b/packages/pharos/package.json @@ -3,7 +3,7 @@ "publishConfig": { "access": "public" }, - "version": "12.26.2", + "version": "13.0.0", "description": "Pharos web components for products and experiences", "files": [ "lib",