Skip to content

Commit

Permalink
fix storybook and package-lock brittleness by removing shoelace d…
Browse files Browse the repository at this point in the history
…ependency (#2686)

* - remove shoelace from dependencies and vendor the files needed
- update ModalContainer to import these without doing so `onMount`
- rebuild package-lock from scratch

* prettier fixes

* regenerate package-lock again

* lint fix

* suppress svelte-check error

* fix: don't lint built files

* fix: lock down web-common yaml dep

---------

Co-authored-by: Speros Kokenes <[email protected]>
  • Loading branch information
bcolloran and skokenes authored Jun 29, 2023
1 parent ab2dc9d commit 00c3006
Show file tree
Hide file tree
Showing 14 changed files with 14,456 additions and 10,560 deletions.
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
web-local/build
24,766 changes: 14,221 additions & 10,545 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion web-common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@
"svelte": "^3.48.0",
"svelte-preprocess": "^5.0.0",
"typescript": "^4.7.4",
"vitest": "^0.31.0"
"vitest": "^0.31.0",
"yaml": "^2.1.3"
},
"type": "module"
}
3 changes: 3 additions & 0 deletions web-common/src/components/date-picker/Litepicker.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@
endEl,
});
// suppress svelte-check error:
// `Error: Property 'ui' is protected and only accessible within class 'LPCore' and its subclasses.`
// @ts-ignore
picker.ui.addEventListener("click", (evt) => {
evt.preventDefault();
evt.stopPropagation();
Expand Down
16 changes: 3 additions & 13 deletions web-common/src/components/modal/ModalContainer.svelte
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
<script lang="ts">
import { createEventDispatcher, onDestroy, onMount } from "svelte";
import { createEventDispatcher, onDestroy } from "svelte";
import { fly } from "svelte/transition";
import Portal from "../Portal.svelte";
import Overlay from "./Overlay.svelte";
import Modal from "./modal";
import { lockBodyScrolling, unlockBodyScrolling } from "./scroll";
const dispatch = createEventDispatcher();
Expand All @@ -13,18 +15,6 @@
let autoFocusTarget;
let containerMountedInPortal = false;
let Modal;
let lockBodyScrolling;
let unlockBodyScrolling;
onMount(async () => {
const scroll = await import(
"@shoelace-style/shoelace/dist/internal/scroll"
);
lockBodyScrolling = scroll.lockBodyScrolling;
unlockBodyScrolling = scroll.unlockBodyScrolling;
Modal = (await import("@shoelace-style/shoelace/dist/internal/modal"))
.default;
});
/** post-mount, and post-portal (which is to say, as soon as container is actually mounted)
* let's go ahead and instantiate the modal.
Expand Down
12 changes: 12 additions & 0 deletions web-common/src/components/modal/modal.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export default class Modal {
element: HTMLElement;
tabDirection: "forward" | "backward";
constructor(element: HTMLElement);
activate(): void;
deactivate(): void;
isActive(): boolean;
checkFocus(): void;
handleFocusIn(): void;
handleKeyDown(event: KeyboardEvent): void;
handleKeyUp(): void;
}
53 changes: 53 additions & 0 deletions web-common/src/components/modal/modal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { getTabbableBoundary } from "./tabbable.js";
let activeModals = [];
export default class Modal {
constructor(element) {
this.tabDirection = "forward";
this.element = element;
this.handleFocusIn = this.handleFocusIn.bind(this);
this.handleKeyDown = this.handleKeyDown.bind(this);
this.handleKeyUp = this.handleKeyUp.bind(this);
}
activate() {
activeModals.push(this.element);
document.addEventListener("focusin", this.handleFocusIn);
document.addEventListener("keydown", this.handleKeyDown);
document.addEventListener("keyup", this.handleKeyUp);
}
deactivate() {
activeModals = activeModals.filter((modal) => modal !== this.element);
document.removeEventListener("focusin", this.handleFocusIn);
document.removeEventListener("keydown", this.handleKeyDown);
document.removeEventListener("keyup", this.handleKeyUp);
}
isActive() {
return activeModals[activeModals.length - 1] === this.element;
}
checkFocus() {
if (this.isActive()) {
if (!this.element.matches(":focus-within")) {
const { start, end } = getTabbableBoundary(this.element);
const target = this.tabDirection === "forward" ? start : end;
if (
typeof (target === null || target === void 0
? void 0
: target.focus) === "function"
) {
target.focus({ preventScroll: true });
}
}
}
}
handleFocusIn() {
this.checkFocus();
}
handleKeyDown(event) {
if (event.key === "Tab" && event.shiftKey) {
this.tabDirection = "backward";
requestAnimationFrame(() => this.checkFocus());
}
}
handleKeyUp() {
this.tabDirection = "forward";
}
}
7 changes: 7 additions & 0 deletions web-common/src/components/modal/offset.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export declare function getOffset(
element: HTMLElement,
parent: HTMLElement
): {
top: number;
left: number;
};
10 changes: 10 additions & 0 deletions web-common/src/components/modal/offset.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export function getOffset(element, parent) {
return {
top: Math.round(
element.getBoundingClientRect().top - parent.getBoundingClientRect().top
),
left: Math.round(
element.getBoundingClientRect().left - parent.getBoundingClientRect().left
),
};
}
8 changes: 8 additions & 0 deletions web-common/src/components/modal/scroll.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export declare function lockBodyScrolling(lockingEl: HTMLElement): void;
export declare function unlockBodyScrolling(lockingEl: HTMLElement): void;
export declare function scrollIntoView(
element: HTMLElement,
container: HTMLElement,
direction?: "horizontal" | "vertical" | "both",
behavior?: "smooth" | "auto"
): void;
58 changes: 58 additions & 0 deletions web-common/src/components/modal/scroll.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { getOffset } from "./offset.js";
const locks = new Set();
function getScrollbarWidth() {
const documentWidth = document.documentElement.clientWidth;
return Math.abs(window.innerWidth - documentWidth);
}
export function lockBodyScrolling(lockingEl) {
locks.add(lockingEl);
if (!document.body.classList.contains("sl-scroll-lock")) {
const scrollbarWidth = getScrollbarWidth();
document.body.classList.add("sl-scroll-lock");
document.body.style.setProperty(
"--sl-scroll-lock-size",
`${scrollbarWidth}px`
);
}
}
export function unlockBodyScrolling(lockingEl) {
locks.delete(lockingEl);
if (locks.size === 0) {
document.body.classList.remove("sl-scroll-lock");
document.body.style.removeProperty("--sl-scroll-lock-size");
}
}
export function scrollIntoView(
element,
container,
direction = "vertical",
behavior = "smooth"
) {
const offset = getOffset(element, container);
const offsetTop = offset.top + container.scrollTop;
const offsetLeft = offset.left + container.scrollLeft;
const minX = container.scrollLeft;
const maxX = container.scrollLeft + container.offsetWidth;
const minY = container.scrollTop;
const maxY = container.scrollTop + container.offsetHeight;
if (direction === "horizontal" || direction === "both") {
if (offsetLeft < minX) {
container.scrollTo({ left: offsetLeft, behavior });
} else if (offsetLeft + element.clientWidth > maxX) {
container.scrollTo({
left: offsetLeft - container.offsetWidth + element.clientWidth,
behavior,
});
}
}
if (direction === "vertical" || direction === "both") {
if (offsetTop < minY) {
container.scrollTo({ top: offsetTop, behavior });
} else if (offsetTop + element.clientHeight > maxY) {
container.scrollTo({
top: offsetTop - container.offsetHeight + element.clientHeight,
behavior,
});
}
}
}
4 changes: 4 additions & 0 deletions web-common/src/components/modal/tabbable.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export declare function getTabbableBoundary(root: HTMLElement | ShadowRoot): {
start: HTMLElement | null;
end: HTMLElement | null;
};
74 changes: 74 additions & 0 deletions web-common/src/components/modal/tabbable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
function isTabbable(el) {
const tag = el.tagName.toLowerCase();
if (el.getAttribute("tabindex") === "-1") {
return false;
}
if (el.hasAttribute("disabled")) {
return false;
}
if (
el.hasAttribute("aria-disabled") &&
el.getAttribute("aria-disabled") !== "false"
) {
return false;
}
if (
tag === "input" &&
el.getAttribute("type") === "radio" &&
!el.hasAttribute("checked")
) {
return false;
}
if (el.offsetParent === null) {
return false;
}
if (window.getComputedStyle(el).visibility === "hidden") {
return false;
}
if ((tag === "audio" || tag === "video") && el.hasAttribute("controls")) {
return true;
}
if (el.hasAttribute("tabindex")) {
return true;
}
if (
el.hasAttribute("contenteditable") &&
el.getAttribute("contenteditable") !== "false"
) {
return true;
}
return [
"button",
"input",
"select",
"textarea",
"a",
"audio",
"video",
"summary",
].includes(tag);
}
export function getTabbableBoundary(root) {
var _a, _b;
const allElements = [];
function walk(el) {
if (el instanceof HTMLElement) {
allElements.push(el);
if (el.shadowRoot !== null && el.shadowRoot.mode === "open") {
walk(el.shadowRoot);
}
}
[...el.children].forEach((e) => walk(e));
}
walk(root);
const start =
(_a = allElements.find((el) => isTabbable(el))) !== null && _a !== void 0
? _a
: null;
const end =
(_b = allElements.reverse().find((el) => isTabbable(el))) !== null &&
_b !== void 0
? _b
: null;
return { start, end };
}
1 change: 0 additions & 1 deletion web-local/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
"@codemirror/view": "^0.20.0",
"@playwright/test": "^1.33.0",
"@rollup/plugin-typescript": "^8.3.1",
"@shoelace-style/shoelace": "^2.0.0-beta.81",
"@stdlib/random-base": "^0.0.6",
"@stdlib/random-shuffle": "^0.0.7",
"@sveltejs/adapter-static": "^1.0.0",
Expand Down

1 comment on commit 00c3006

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.