@justeattakeaway/pie-webc-core
Advanced tools
Comparing version 0.0.0-snapshot-release-20240411081540 to 0.0.0-snapshot-release-20240501131344
# Changelog | ||
## 0.0.0-snapshot-release-20240411081540 | ||
## 0.0.0-snapshot-release-20240501131344 | ||
### Minor Changes | ||
- [Added] - PieFormManager class to handle invalid input focusing ([#1374](https://github.com/justeattakeaway/pie/pull/1374)) by [@jamieomaguire](https://github.com/jamieomaguire) | ||
- [Changed] - Updated Lit from 3.1.2 to 3.1.3 ([#1427](https://github.com/justeattakeaway/pie/pull/1427)) by [@jamieomaguire](https://github.com/jamieomaguire) | ||
## 0.21.1 | ||
### Patch Changes | ||
- [Removed] - Unneeded `bundledJetDeps` array (patch versions are required because pie-components-config is a dev dependency) ([#1378](https://github.com/justeattakeaway/pie/pull/1378)) by [@xander-marjoram](https://github.com/xander-marjoram) | ||
## 0.21.0 | ||
### Minor Changes | ||
- [Added] - Bundle visualiser that runs during build for webc icons, webc core and our components ([#1391](https://github.com/justeattakeaway/pie/pull/1391)) by [@jamieomaguire](https://github.com/jamieomaguire) | ||
## 0.20.0 | ||
@@ -10,0 +22,0 @@ |
import { isServer as c } from "lit"; | ||
const m = (t, e, n) => function(s, i) { | ||
const o = `#${i}`; | ||
Object.defineProperty(s, i, { | ||
const a = (t, e, r) => function(s, n) { | ||
const o = `#${n}`; | ||
Object.defineProperty(s, n, { | ||
get() { | ||
return this[o]; | ||
}, | ||
set(a) { | ||
e.includes(a) ? this[o] = a : (console.error( | ||
`<${t}> Invalid value "${a}" provided for property "${i}".`, | ||
set(u) { | ||
e.includes(u) ? this[o] = u : (console.error( | ||
`<${t}> Invalid value "${u}" provided for property "${n}".`, | ||
`Must be one of: ${e.join(" | ")}.`, | ||
`Falling back to default value: "${n}"` | ||
), this[o] = n); | ||
`Falling back to default value: "${r}"` | ||
), this[o] = r); | ||
}, | ||
configurable: !0 | ||
}); | ||
}, f = (t) => function(n, r) { | ||
const s = `#${r}`; | ||
Object.defineProperty(n, r, { | ||
}, d = (t) => function(r, i) { | ||
const s = `#${i}`; | ||
Object.defineProperty(r, i, { | ||
get() { | ||
return this[s]; | ||
}, | ||
set(i) { | ||
(i == null || typeof i == "string" && i.trim() === "") && console.error(`<${t}> Missing required attribute "${r}"`), this[s] = i; | ||
set(n) { | ||
(n == null || typeof n == "string" && n.trim() === "") && console.error(`<${t}> Missing required attribute "${i}"`), this[s] = n; | ||
}, | ||
@@ -29,6 +29,6 @@ configurable: !0 | ||
}; | ||
function p(t, e) { | ||
function f(t, e) { | ||
customElements.get(t) ? console.warn(`PIE Web Component: "${t}" has already been defined. Please ensure the component is only being defined once in your application.`) : customElements.define(t, e); | ||
} | ||
function h(t) { | ||
function m(t) { | ||
return new CustomEvent(t.type, { | ||
@@ -42,12 +42,12 @@ detail: { | ||
} | ||
function g(t, e, n) { | ||
function b(t, e, r) { | ||
e.startsWith("pie-") || console.warn("A custom event name should start with `pie-`"); | ||
const r = new CustomEvent(e, { | ||
const i = new CustomEvent(e, { | ||
bubbles: !0, | ||
composed: !0, | ||
detail: n | ||
detail: r | ||
}); | ||
t.dispatchEvent(r); | ||
t.dispatchEvent(i); | ||
} | ||
const b = (t) => { | ||
const h = (t) => { | ||
class e extends t { | ||
@@ -68,53 +68,7 @@ /** | ||
return e; | ||
}; | ||
function l(t, e) { | ||
var s; | ||
const n = t.querySelectorAll("pie-input"), r = Array.from(n).find((i) => !i.validity.valid); | ||
r && (e.preventDefault(), (s = r.focusTarget) == null || s.focus()); | ||
} | ||
class u { | ||
constructor() { | ||
this._forms = /* @__PURE__ */ new WeakMap(); | ||
} | ||
/** | ||
* Performs any necessary operations when a form is submitted. | ||
*/ | ||
handleSubmit(e) { | ||
const n = e.target; | ||
l(n, e); | ||
} | ||
/** | ||
* Adds a form to the form manager and attaches a submit event listener to it. | ||
*/ | ||
addForm(e) { | ||
if (this.getForm(e)) | ||
return; | ||
const r = { | ||
listener: this.handleSubmit, | ||
form: e | ||
}; | ||
e.addEventListener("submit", r.listener), this._forms.set(e, r); | ||
} | ||
/** | ||
* Removes a form from the form manager and the submit event listener from it. | ||
* @param form - The form to remove from the form manager. | ||
*/ | ||
deleteForm(e) { | ||
const n = this.getForm(e); | ||
n != null && n.listener && e.removeEventListener("submit", n.listener), this._forms.delete(e); | ||
} | ||
/** | ||
* Retreives the PieFormData object associated with a form. | ||
* @param form - The form to retrieve the PieFormData object of. | ||
* @returns the PieFormData object associated with the form. | ||
*/ | ||
getForm(e) { | ||
return this._forms.get(e); | ||
} | ||
} | ||
const F = (t) => { | ||
}, p = (t) => { | ||
class e extends t { | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
constructor(...r) { | ||
super(...r), this._internals = this.attachInternals(); | ||
constructor(...i) { | ||
super(...i), this._internals = this.attachInternals(); | ||
} | ||
@@ -124,9 +78,2 @@ get form() { | ||
} | ||
connectedCallback() { | ||
var r; | ||
super.connectedCallback(), this.form && (window.pieFormManager || (window.pieFormManager = new u()), window.pieFormManager.addForm(this.form), this._managedForm = (r = window.pieFormManager.getForm(this.form)) == null ? void 0 : r.form); | ||
} | ||
disconnectedCallback() { | ||
super.disconnectedCallback(), this._managedForm && window.pieFormManager && this._managedForm.querySelectorAll("pie-input").length === 0 && window.pieFormManager.deleteForm(this._managedForm); | ||
} | ||
} | ||
@@ -136,10 +83,9 @@ return e.formAssociated = !0, e; | ||
export { | ||
F as FormControlMixin, | ||
u as PieFormManager, | ||
b as RtlMixin, | ||
p as defineCustomElement, | ||
g as dispatchCustomEvent, | ||
f as requiredProperty, | ||
m as validPropertyValues, | ||
h as wrapNativeEvent | ||
p as FormControlMixin, | ||
h as RtlMixin, | ||
f as defineCustomElement, | ||
b as dispatchCustomEvent, | ||
d as requiredProperty, | ||
a as validPropertyValues, | ||
m as wrapNativeEvent | ||
}; |
{ | ||
"name": "@justeattakeaway/pie-webc-core", | ||
"version": "0.0.0-snapshot-release-20240411081540", | ||
"version": "0.0.0-snapshot-release-20240501131344", | ||
"description": "PIE design system base classes, mixins and utilities for web components", | ||
@@ -21,6 +21,6 @@ "type": "module", | ||
"dependencies": { | ||
"lit": "3.1.2" | ||
"lit": "3.1.3" | ||
}, | ||
"devDependencies": { | ||
"@justeattakeaway/pie-components-config": "0.14.0" | ||
"@justeattakeaway/pie-components-config": "0.16.0" | ||
}, | ||
@@ -27,0 +27,0 @@ "volta": { |
@@ -19,2 +19,3 @@ <p align="center"> | ||
6. [Testing](#testing) | ||
7. [Bundling](#bundling) | ||
@@ -76,1 +77,4 @@ ## Introduction | ||
Currently, for writing unit tests we simply name the file `**/*.spec.ts`. To write browser tests, we name the file `**/*.browser.spec.ts`. This allows us to run all unit tests using `yarn test --filter=pie-webc-core` and all browser tests using `yarn test:browsers --filter=pie-webc-core`. | ||
## Bundling | ||
When we build the package, we run a plugin for Rollup named `rollup-plugin-visualizer`. This generates a file named `stats.html` in the root of the package. This file can be viewed in the browser to visualise the bundled Javascript and better understand what contributes to the size of the final build output. |
@@ -5,2 +5,1 @@ export * from './decorators'; | ||
export * from './mixins'; | ||
export * from './forms'; |
export interface PIEInputElement { | ||
focusTarget: HTMLElement; | ||
validity: ValidityState; | ||
focus(): void; | ||
} |
import type { LitElement } from 'lit'; | ||
import type { GenericConstructor } from '../types/GenericConstructor'; | ||
import { PieFormManager } from '../../forms/pie-form-manager'; | ||
@@ -51,5 +50,2 @@ /** | ||
// Storing this reference as this.form is not available in disconnectedCallback | ||
private _managedForm?: HTMLFormElement; | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
@@ -60,32 +56,2 @@ constructor (...args: any[]) { | ||
} | ||
override connectedCallback (): void { | ||
super.connectedCallback(); | ||
if (this.form) { | ||
// Ensure that the form is managed by the PieFormManager | ||
if (!window.pieFormManager) { | ||
window.pieFormManager = new PieFormManager(); | ||
} | ||
window.pieFormManager.addForm(this.form); | ||
// Storing a reference to the form to be used in disconnectedCallback when this.form will be unavailable. | ||
this._managedForm = window.pieFormManager.getForm(this.form)?.form; | ||
} | ||
} | ||
override disconnectedCallback (): void { | ||
super.disconnectedCallback(); | ||
if (this._managedForm && window.pieFormManager) { | ||
// We can extend this to include more form control components as we create them. | ||
const pieInputsInForm = this._managedForm.querySelectorAll('pie-input'); | ||
// This particular component instance is not queryable in the DOM during disconnectedCallback | ||
// so if it was the last require field then the length would be 0 | ||
if (pieInputsInForm.length === 0) { | ||
window.pieFormManager.deleteForm(this._managedForm); | ||
} | ||
} | ||
} | ||
} | ||
@@ -92,0 +58,0 @@ |
@@ -28,12 +28,2 @@ import { test, expect } from '@sand4rt/experimental-ct-web'; | ||
}); | ||
test('should not instantiate a PieFormManager instance', async ({ mount, page }) => { | ||
// Arrange | ||
await mount(MockComponent); | ||
const formManagerExists = await page.evaluate(() => !!window.pieFormManager); | ||
// Assert | ||
expect(formManagerExists).toBe(false); | ||
}); | ||
}); | ||
@@ -62,88 +52,2 @@ | ||
}); | ||
test.describe('PieFormManager integration', () => { | ||
test('should instantiate a PieFormManager instance if one does not exist and attach it to the window', async ({ mount, page }) => { | ||
// Arrange | ||
await page.setContent(` | ||
<form id="testForm" action="/foo" method="POST"> | ||
<form-control-mixin-mock></form-control-mixin-mock> | ||
</form> | ||
`); | ||
const formManagerExists = await page.evaluate(() => !!window.pieFormManager); | ||
// Assert | ||
expect(formManagerExists).toBe(true); | ||
}); | ||
test('should add the associated form to the PieFormManager instance', async ({ page }) => { | ||
// Arrange | ||
await page.setContent(` | ||
<form id="testForm" action="/foo" method="POST"> | ||
<form-control-mixin-mock></form-control-mixin-mock> | ||
</form> | ||
`); | ||
const formExists = await page.evaluate(() => { | ||
const form = document.getElementById('testForm') as HTMLFormElement; | ||
const formManager = window.pieFormManager; | ||
return !!formManager?.getForm(form); | ||
}); | ||
// Assert | ||
expect(formExists).toBe(true); | ||
}); | ||
test('should remove the associated form from the PieFormManager instance when disconnected and no pie-input instances are in the form', async ({ page }) => { | ||
// Arrange | ||
await page.setContent(` | ||
<form id="testForm" action="/foo" method="POST"> | ||
<form-control-mixin-mock></form-control-mixin-mock> | ||
</form> | ||
`); | ||
await page.evaluate(() => { | ||
const component = document.querySelector('form-control-mixin-mock'); | ||
component?.remove(); | ||
}); | ||
const formExists = await page.evaluate(() => { | ||
const form = document.getElementById('testForm') as HTMLFormElement; | ||
const formManager = window.pieFormManager; | ||
return !!formManager?.getForm(form); | ||
}); | ||
// Assert | ||
expect(formExists).toBe(false); | ||
}); | ||
test('should not remove the associated form from the PieFormManager instance when disconnected and other form controls remain inside the form', async ({ page }) => { | ||
// Arrange | ||
// We don't actually need pie-input to be defined here, as the tags will still appear in the DOM which the form manager queries. | ||
// Once we add more form control components, we can add them here. | ||
await page.setContent(` | ||
<form id="testForm" action="/foo" method="POST"> | ||
<pie-input></pie-input> | ||
<form-control-mixin-mock id="remove"></form-control-mixin-mock> | ||
<pie-input></pie-input> | ||
<pie-input></pie-input> | ||
</form> | ||
`); | ||
const formExists = await page.evaluate(() => { | ||
const component = document.querySelector('#remove'); | ||
component?.remove(); | ||
const form = document.getElementById('testForm') as HTMLFormElement; | ||
const formManager = window.pieFormManager; | ||
return !!formManager?.getForm(form); | ||
}); | ||
// Assert | ||
expect(formExists).toBe(true); | ||
}); | ||
}); | ||
}); | ||
@@ -172,20 +76,4 @@ | ||
}); | ||
test('should not instantiate a PieFormManager instance', async ({ mount, page }) => { | ||
// Arrange | ||
await page.setContent(` | ||
<form id="siblingForm" action="/foo" method="POST"> | ||
<input type="text" id="username" name="username" required> | ||
<input type="password" id="password" name="password" required> | ||
</form> | ||
<form-control-mixin-mock></form-control-mixin-mock> | ||
`); | ||
const formManagerExists = await page.evaluate(() => !!window.pieFormManager); | ||
// Assert | ||
expect(formManagerExists).toBe(false); | ||
}); | ||
}); | ||
}); | ||
}); |
import { defineConfig } from 'vite'; | ||
import { visualizer } from 'rollup-plugin-visualizer'; | ||
@@ -25,2 +26,8 @@ export default defineConfig({ | ||
}, | ||
plugins: [ | ||
visualizer({ | ||
gzipSize: true, | ||
brotliSize: true, | ||
}) | ||
], | ||
}); |
Sorry, the diff of this file is not supported yet
79
45891
35
881