diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 716f5478..5d223a9e 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -49,6 +49,13 @@ module.exports = { '@typescript-eslint/no-unused-expressions': 'error', '@typescript-eslint/unbound-method': 'off', '@typescript-eslint/no-non-null-assertion': 'off', + '@typescript-eslint/no-floating-promises': 'off', + '@typescript-eslint/no-misused-promises': [ + 'error', + { + checksVoidReturn: false + } + ], '@typescript-eslint/consistent-type-assertions': [ 'warn', { @@ -57,6 +64,7 @@ module.exports = { } ], '@typescript-eslint/consistent-type-imports': 'warn', + // These are commented out for now as we may want to add them to improve function boundary safety // "@typescript-eslint/explicit-function-return-type": [ // "error", // { @@ -67,7 +75,6 @@ module.exports = { // "@typescript-eslint/explicit-module-boundary-types": "error", '@typescript-eslint/no-base-to-string': 'error', '@typescript-eslint/no-confusing-non-null-assertion': 'error', - '@typescript-eslint/no-confusing-void-expression': 'error', '@typescript-eslint/no-invalid-void-type': 'error', '@typescript-eslint/no-require-imports': 'error', '@typescript-eslint/no-unnecessary-boolean-literal-compare': 'warn', @@ -108,6 +115,10 @@ module.exports = { { selector: 'typeProperty', format: ['camelCase', 'PascalCase', 'UPPER_CASE'] + }, + { + selector: 'objectLiteralProperty', + format: null } ], '@typescript-eslint/no-extraneous-class': 'error', diff --git a/custom-elements-manifest.config.js b/custom-elements-manifest.config.js index fab08e03..deae4016 100644 --- a/custom-elements-manifest.config.js +++ b/custom-elements-manifest.config.js @@ -4,10 +4,8 @@ import { pascalCase } from 'pascal-case'; const packageData = JSON.parse(fs.readFileSync('./package.json', 'utf8')); const { name, description, version, author, homepage, license } = packageData; - -function noDash(string) { - return string.replace(/^\s?-/, '').trim(); -} +// eslint-disable-next-line func-style +const noDash = string => string.replace(/^\s?-/, '').trim(); export default { globs: ['src/components/**/*.ts'], diff --git a/package-lock.json b/package-lock.json index 96b28562..8e237a19 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,6 @@ "@shoelace-style/localize": "^2.1.3", "color": "4.1", "lit": "^2.1.0", - "lit-html": "^2.1.1", "qr-creator": "^1.0.0" }, "devDependencies": { @@ -24,8 +23,8 @@ "@types/color": "^3.0.2", "@types/mocha": "^9.0.0", "@types/react": "^17.0.38", - "@typescript-eslint/eslint-plugin": "^5.9.0", - "@typescript-eslint/parser": "^5.9.0", + "@typescript-eslint/eslint-plugin": "^5.9.1", + "@typescript-eslint/parser": "^5.9.1", "@web/dev-server-esbuild": "^0.2.16", "@web/test-runner": "^0.13.23", "@web/test-runner-playwright": "^0.8.8", @@ -38,7 +37,7 @@ "del": "^6.0.0", "download": "^8.0.0", "esbuild": "^0.14.10", - "eslint": "^8.6.0", + "eslint": "8.6.0", "eslint-plugin-chai-expect": "^3.0.0", "eslint-plugin-chai-friendly": "^0.7.2", "eslint-plugin-import": "^2.25.4", @@ -60,8 +59,7 @@ "sinon": "^12.0.1", "strip-css-comments": "^5.0.0", "tslib": "^2.3.1", - "typescript": "^4.5.4", - "utility-types": "^3.10.0" + "typescript": "^4.5.4" }, "engines": { "node": ">=14.15.0" @@ -2337,6 +2335,15 @@ "string-width": "^4.1.0" } }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -5136,6 +5143,18 @@ } } }, + "node_modules/enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/env-paths": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", @@ -5536,9 +5555,9 @@ } }, "node_modules/eslint": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.7.0.tgz", - "integrity": "sha512-ifHYzkBGrzS2iDU7KjhCAVMGCvF6M3Xfs8X8b37cgrUlDt6bWRTpRh6T/gtSXv1HJ/BUGgmjvNvOEGu85Iif7w==", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.6.0.tgz", + "integrity": "sha512-UvxdOJ7mXFlw7iuHZA4jmzPaUqIw54mZrv+XPYKNbKdLR0et4rf60lIZUU9kiNtnzzMzGWxMV+tQ7uG7JG8DPw==", "dev": true, "dependencies": { "@eslint/eslintrc": "^1.0.5", @@ -5548,10 +5567,11 @@ "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", + "enquirer": "^2.3.5", "escape-string-regexp": "^4.0.0", "eslint-scope": "^7.1.0", "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.2.0", + "eslint-visitor-keys": "^3.1.0", "espree": "^9.3.0", "esquery": "^1.4.0", "esutils": "^2.0.2", @@ -5560,7 +5580,7 @@ "functional-red-black-tree": "^1.0.1", "glob-parent": "^6.0.1", "globals": "^13.6.0", - "ignore": "^5.2.0", + "ignore": "^4.0.6", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", @@ -5571,7 +5591,9 @@ "minimatch": "^3.0.4", "natural-compare": "^1.4.0", "optionator": "^0.9.1", + "progress": "^2.0.0", "regexpp": "^3.2.0", + "semver": "^7.2.1", "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", "text-table": "^0.2.0", @@ -5960,6 +5982,15 @@ "node": ">=10.13.0" } }, + "node_modules/eslint/node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, "node_modules/eslint/node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -13567,15 +13598,6 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, - "node_modules/utility-types": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz", - "integrity": "sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -15920,6 +15942,12 @@ "string-width": "^4.1.0" } }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, "ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -18080,6 +18108,15 @@ "has-binary2": "~1.0.2" } }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.1" + } + }, "env-paths": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", @@ -18338,9 +18375,9 @@ "dev": true }, "eslint": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.7.0.tgz", - "integrity": "sha512-ifHYzkBGrzS2iDU7KjhCAVMGCvF6M3Xfs8X8b37cgrUlDt6bWRTpRh6T/gtSXv1HJ/BUGgmjvNvOEGu85Iif7w==", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.6.0.tgz", + "integrity": "sha512-UvxdOJ7mXFlw7iuHZA4jmzPaUqIw54mZrv+XPYKNbKdLR0et4rf60lIZUU9kiNtnzzMzGWxMV+tQ7uG7JG8DPw==", "dev": true, "requires": { "@eslint/eslintrc": "^1.0.5", @@ -18350,10 +18387,11 @@ "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", + "enquirer": "^2.3.5", "escape-string-regexp": "^4.0.0", "eslint-scope": "^7.1.0", "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.2.0", + "eslint-visitor-keys": "^3.1.0", "espree": "^9.3.0", "esquery": "^1.4.0", "esutils": "^2.0.2", @@ -18362,7 +18400,7 @@ "functional-red-black-tree": "^1.0.1", "glob-parent": "^6.0.1", "globals": "^13.6.0", - "ignore": "^5.2.0", + "ignore": "^4.0.6", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", @@ -18373,7 +18411,9 @@ "minimatch": "^3.0.4", "natural-compare": "^1.4.0", "optionator": "^0.9.1", + "progress": "^2.0.0", "regexpp": "^3.2.0", + "semver": "^7.2.1", "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", "text-table": "^0.2.0", @@ -18430,6 +18470,12 @@ "is-glob": "^4.0.3" } }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, "js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -24522,12 +24568,6 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, - "utility-types": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz", - "integrity": "sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==", - "dev": true - }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", diff --git a/package.json b/package.json index 34bf447d..ceed5b10 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,6 @@ "@shoelace-style/localize": "^2.1.3", "color": "4.1", "lit": "^2.1.0", - "lit-html": "^2.1.1", "qr-creator": "^1.0.0" }, "devDependencies": { @@ -65,8 +64,8 @@ "@types/color": "^3.0.2", "@types/mocha": "^9.0.0", "@types/react": "^17.0.38", - "@typescript-eslint/eslint-plugin": "^5.9.0", - "@typescript-eslint/parser": "^5.9.0", + "@typescript-eslint/eslint-plugin": "^5.9.1", + "@typescript-eslint/parser": "^5.9.1", "@web/dev-server-esbuild": "^0.2.16", "@web/test-runner": "^0.13.23", "@web/test-runner-playwright": "^0.8.8", @@ -79,7 +78,7 @@ "del": "^6.0.0", "download": "^8.0.0", "esbuild": "^0.14.10", - "eslint": "^8.6.0", + "eslint": "8.6.0", "eslint-plugin-chai-expect": "^3.0.0", "eslint-plugin-chai-friendly": "^0.7.2", "eslint-plugin-import": "^2.25.4", @@ -101,7 +100,6 @@ "sinon": "^12.0.1", "strip-css-comments": "^5.0.0", "tslib": "^2.3.1", - "typescript": "^4.5.4", - "utility-types": "^3.10.0" + "typescript": "^4.5.4" } } diff --git a/src/components/alert/alert.test.ts b/src/components/alert/alert.test.ts index 7030371e..8870d3d3 100644 --- a/src/components/alert/alert.test.ts +++ b/src/components/alert/alert.test.ts @@ -25,7 +25,7 @@ describe('', () => { el.addEventListener('sl-show', showHandler); el.addEventListener('sl-after-show', afterShowHandler); - void el.show(); + el.show(); await waitUntil(() => showHandler.calledOnce); await waitUntil(() => afterShowHandler.calledOnce); @@ -43,7 +43,7 @@ describe('', () => { el.addEventListener('sl-hide', hideHandler); el.addEventListener('sl-after-hide', afterHideHandler); - void el.hide(); + el.hide(); await waitUntil(() => hideHandler.calledOnce); await waitUntil(() => afterHideHandler.calledOnce); diff --git a/src/components/alert/alert.ts b/src/components/alert/alert.ts index 7b362431..69c2bf2c 100644 --- a/src/components/alert/alert.ts +++ b/src/components/alert/alert.ts @@ -39,7 +39,7 @@ const toastStack = Object.assign(document.createElement('div'), { className: 'sl export default class SlAlert extends LitElement { static styles = styles; - private autoHideTimeout: NodeJS.Timeout; + private autoHideTimeout: number; @query('[part="base"]') base: HTMLElement; @@ -99,7 +99,7 @@ export default class SlAlert extends LitElement { requestAnimationFrame(() => { // eslint-disable-next-line @typescript-eslint/no-unused-expressions -- force a reflow for the initial transition this.clientWidth; - void this.show(); + this.show(); }); this.addEventListener( @@ -121,14 +121,12 @@ export default class SlAlert extends LitElement { restartAutoHide() { clearTimeout(this.autoHideTimeout); if (this.open && this.duration < Infinity) { - this.autoHideTimeout = setTimeout(() => { - void this.hide(); - }, this.duration); + this.autoHideTimeout = window.setTimeout(() => this.hide(), this.duration); } } handleCloseClick() { - void this.hide(); + this.hide(); } handleMouseMove() { diff --git a/src/components/animation/animation.ts b/src/components/animation/animation.ts index 38094973..fee72456 100644 --- a/src/components/animation/animation.ts +++ b/src/components/animation/animation.ts @@ -84,7 +84,7 @@ export default class SlAnimation extends LitElement { connectedCallback() { super.connectedCallback(); - void this.createAnimation(); + this.createAnimation(); this.handleAnimationCancel = this.handleAnimationCancel.bind(this); this.handleAnimationFinish = this.handleAnimationFinish.bind(this); } @@ -109,7 +109,7 @@ export default class SlAnimation extends LitElement { return; } - void this.createAnimation(); + this.createAnimation(); } handleAnimationFinish() { @@ -152,7 +152,7 @@ export default class SlAnimation extends LitElement { handleSlotChange() { this.destroyAnimation(); - void this.createAnimation(); + this.createAnimation(); } async createAnimation() { diff --git a/src/components/avatar/avatar.ts b/src/components/avatar/avatar.ts index 84e6f681..eb5a0d25 100644 --- a/src/components/avatar/avatar.ts +++ b/src/components/avatar/avatar.ts @@ -3,6 +3,7 @@ import { customElement, property, state } from 'lit/decorators.js'; import { classMap } from 'lit/directives/class-map.js'; import styles from './avatar.styles'; import '~/components/icon/icon'; +import { isTruthy } from '~/internal/is-truthy'; /** * @since 2.0 @@ -50,7 +51,7 @@ export default class SlAvatar extends LitElement { role="img" aria-label=${this.label} > - ${typeof this.initials !== 'undefined' + ${isTruthy(this.initials) ? html`
${this.initials}
` : html` `} - ${typeof this.image !== 'undefined' && !this.hasError + ${typeof this.image === 'string' && !this.hasError ? html` diff --git a/src/components/breadcrumb/breadcrumb.ts b/src/components/breadcrumb/breadcrumb.ts index 7936de7e..605286fc 100644 --- a/src/components/breadcrumb/breadcrumb.ts +++ b/src/components/breadcrumb/breadcrumb.ts @@ -34,9 +34,7 @@ export default class SlBreadcrumb extends LitElement { // Clone it, remove ids, and slot it const clone = separator.cloneNode(true) as HTMLElement; - [clone, ...clone.querySelectorAll('[id]')].forEach(el => { - el.removeAttribute('id'); - }); + [clone, ...clone.querySelectorAll('[id]')].forEach(el => el.removeAttribute('id')); clone.slot = 'separator'; return clone; diff --git a/src/components/button-group/button-group.ts b/src/components/button-group/button-group.ts index 436c21c5..c47410b4 100644 --- a/src/components/button-group/button-group.ts +++ b/src/components/button-group/button-group.ts @@ -56,7 +56,7 @@ export default class SlButtonGroup extends LitElement { } render() { - // eslint-disable-next-line lit-a11y/mouse-events-have-key-events -- focusout & focusin support bubbling where as focus & blur do not which is necessary here + // eslint-disable-next-line lit-a11y/mouse-events-have-key-events -- focusout & focusin support bubbling whereas focus & blur do not which is necessary here return html`
', () => { it('should be disabled with the disabled attribute', async () => { const el = await fixture(html` `); - const checkbox = el.shadowRoot?.querySelector('input'); + const checkbox = el.shadowRoot!.querySelector('input')!; - expect(checkbox!.disabled).to.be.true; + expect(checkbox.disabled).to.be.true; }); it('should be valid by default', async () => { @@ -18,9 +18,7 @@ describe('', () => { it('should fire sl-change when clicked', async () => { const el = await fixture(html` `); - setTimeout(() => { - el.shadowRoot!.querySelector('input')!.click(); - }); + setTimeout(() => el.shadowRoot!.querySelector('input')!.click()); const event = await oneEvent(el, 'sl-change'); expect(event.target).to.equal(el); expect(el.checked).to.be.true; @@ -30,9 +28,7 @@ describe('', () => { const el = await fixture(html` `); const input = el.shadowRoot!.querySelector('input')!; input.focus(); - setTimeout(() => { - void sendKeys({ press: ' ' }); - }); + setTimeout(() => sendKeys({ press: ' ' })); const event = await oneEvent(el, 'sl-change'); expect(event.target).to.equal(el); expect(el.checked).to.be.true; diff --git a/src/components/checkbox/checkbox.ts b/src/components/checkbox/checkbox.ts index 453c1245..993ebabd 100644 --- a/src/components/checkbox/checkbox.ts +++ b/src/components/checkbox/checkbox.ts @@ -4,7 +4,7 @@ import { classMap } from 'lit/directives/class-map.js'; import { ifDefined } from 'lit/directives/if-defined.js'; import { live } from 'lit/directives/live.js'; import styles from './checkbox.styles'; -import { autoIncrement } from '~/internal/autoIncrement'; +import { autoIncrement } from '~/internal/auto-increment'; import { emit } from '~/internal/event'; import { FormSubmitController } from '~/internal/form-control'; import { watch } from '~/internal/watch'; diff --git a/src/components/color-picker/color-picker.ts b/src/components/color-picker/color-picker.ts index 731b3b65..8ef158f4 100644 --- a/src/components/color-picker/color-picker.ts +++ b/src/components/color-picker/color-picker.ts @@ -161,7 +161,7 @@ export default class SlColorPicker extends LitElement { this.inputValue = this.value; this.lastValueEmitted = this.value; - void this.syncValues(); + this.syncValues(); } /** Returns the current value as a string in the specified format. */ @@ -205,7 +205,7 @@ export default class SlColorPicker extends LitElement { }, { once: true } ); - void this.dropdown.show(); + this.dropdown.show(); }); } return this.input.reportValidity(); @@ -245,7 +245,7 @@ export default class SlColorPicker extends LitElement { drag(container, x => { this.alpha = clamp((x / width) * 100, 0, 100); - void this.syncValues(); + this.syncValues(); }); } @@ -259,7 +259,7 @@ export default class SlColorPicker extends LitElement { drag(container, x => { this.hue = clamp((x / width) * 360, 0, 360); - void this.syncValues(); + this.syncValues(); }); } @@ -274,7 +274,7 @@ export default class SlColorPicker extends LitElement { drag(grid, (x, y) => { this.saturation = clamp((x / width) * 100, 0, 100); this.lightness = clamp(100 - (y / height) * 100, 0, 100); - void this.syncValues(); + this.syncValues(); }); } @@ -284,25 +284,25 @@ export default class SlColorPicker extends LitElement { if (event.key === 'ArrowLeft') { event.preventDefault(); this.alpha = clamp(this.alpha - increment, 0, 100); - void this.syncValues(); + this.syncValues(); } if (event.key === 'ArrowRight') { event.preventDefault(); this.alpha = clamp(this.alpha + increment, 0, 100); - void this.syncValues(); + this.syncValues(); } if (event.key === 'Home') { event.preventDefault(); this.alpha = 0; - void this.syncValues(); + this.syncValues(); } if (event.key === 'End') { event.preventDefault(); this.alpha = 100; - void this.syncValues(); + this.syncValues(); } } @@ -312,25 +312,25 @@ export default class SlColorPicker extends LitElement { if (event.key === 'ArrowLeft') { event.preventDefault(); this.hue = clamp(this.hue - increment, 0, 360); - void this.syncValues(); + this.syncValues(); } if (event.key === 'ArrowRight') { event.preventDefault(); this.hue = clamp(this.hue + increment, 0, 360); - void this.syncValues(); + this.syncValues(); } if (event.key === 'Home') { event.preventDefault(); this.hue = 0; - void this.syncValues(); + this.syncValues(); } if (event.key === 'End') { event.preventDefault(); this.hue = 360; - void this.syncValues(); + this.syncValues(); } } @@ -340,25 +340,25 @@ export default class SlColorPicker extends LitElement { if (event.key === 'ArrowLeft') { event.preventDefault(); this.saturation = clamp(this.saturation - increment, 0, 100); - void this.syncValues(); + this.syncValues(); } if (event.key === 'ArrowRight') { event.preventDefault(); this.saturation = clamp(this.saturation + increment, 0, 100); - void this.syncValues(); + this.syncValues(); } if (event.key === 'ArrowUp') { event.preventDefault(); this.lightness = clamp(this.lightness + increment, 0, 100); - void this.syncValues(); + this.syncValues(); } if (event.key === 'ArrowDown') { event.preventDefault(); this.lightness = clamp(this.lightness - increment, 0, 100); - void this.syncValues(); + this.syncValues(); } } @@ -374,9 +374,7 @@ export default class SlColorPicker extends LitElement { if (event.key === 'Enter') { this.setColor(this.input.value); this.input.value = this.value; - setTimeout(() => { - this.input.select(); - }); + setTimeout(() => this.input.select()); } } @@ -514,7 +512,7 @@ export default class SlColorPicker extends LitElement { this.lightness = newColor.hsla.l; this.alpha = this.opacity ? newColor.hsla.a * 100 : 100; - void this.syncValues(); + this.syncValues(); return true; } @@ -574,7 +572,7 @@ export default class SlColorPicker extends LitElement { @watch('format') handleFormatChange() { - void this.syncValues(); + this.syncValues(); } @watch('opacity') @@ -800,14 +798,12 @@ export default class SlColorPicker extends LitElement { part="trigger" slot="trigger" class=${classMap({ - /* eslint-disable @typescript-eslint/naming-convention */ 'color-dropdown__trigger': true, 'color-dropdown__trigger--disabled': this.disabled, 'color-dropdown__trigger--small': this.size === 'small', 'color-dropdown__trigger--medium': this.size === 'medium', 'color-dropdown__trigger--large': this.size === 'large', 'color-picker__transparent-bg': true - /* eslint-enable @typescript-eslint/naming-convention */ })} style=${styleMap({ color: `hsla(${this.hue}deg, ${this.saturation}%, ${this.lightness}%, ${this.alpha / 100})` diff --git a/src/components/details/details.test.ts b/src/components/details/details.test.ts index 52346413..9143b910 100644 --- a/src/components/details/details.test.ts +++ b/src/components/details/details.test.ts @@ -44,7 +44,7 @@ describe('', () => { el.addEventListener('sl-show', showHandler); el.addEventListener('sl-after-show', afterShowHandler); - void el.show(); + el.show(); await waitUntil(() => showHandler.calledOnce); await waitUntil(() => afterShowHandler.calledOnce); @@ -68,7 +68,7 @@ describe('', () => { el.addEventListener('sl-hide', hideHandler); el.addEventListener('sl-after-hide', afterHideHandler); - void el.hide(); + el.hide(); await waitUntil(() => hideHandler.calledOnce); await waitUntil(() => afterHideHandler.calledOnce); @@ -139,13 +139,13 @@ describe('', () => { `); const first = el.querySelectorAll('sl-details')[0]; const second = el.querySelectorAll('sl-details')[1]; - const firstBody = first.shadowRoot?.querySelector('.details__body'); - const secondBody = second.shadowRoot?.querySelector('.details__body'); + const firstBody = first.shadowRoot!.querySelector('.details__body')!; + const secondBody = second.shadowRoot!.querySelector('.details__body')!; await first.show(); await second.show(); - expect(firstBody!.clientHeight).to.equal(200); - expect(secondBody!.clientHeight).to.equal(400); + expect(firstBody.clientHeight).to.equal(200); + expect(secondBody.clientHeight).to.equal(400); }); }); diff --git a/src/components/details/details.ts b/src/components/details/details.ts index e1815c4c..5bec07cd 100644 --- a/src/components/details/details.ts +++ b/src/components/details/details.ts @@ -75,9 +75,9 @@ export default class SlDetails extends LitElement { toggleOpen() { if (this.open) { - void this.hide(); + this.hide(); } else { - void this.show(); + this.show(); } } @@ -96,12 +96,12 @@ export default class SlDetails extends LitElement { if (event.key === 'ArrowUp' || event.key === 'ArrowLeft') { event.preventDefault(); - void this.hide(); + this.hide(); } if (event.key === 'ArrowDown' || event.key === 'ArrowRight') { event.preventDefault(); - void this.show(); + this.show(); } } diff --git a/src/components/dialog/dialog.test.ts b/src/components/dialog/dialog.test.ts index 23d4d827..f430f08f 100644 --- a/src/components/dialog/dialog.test.ts +++ b/src/components/dialog/dialog.test.ts @@ -32,7 +32,7 @@ describe('', () => { el.addEventListener('sl-show', showHandler); el.addEventListener('sl-after-show', afterShowHandler); - void el.show(); + el.show(); await waitUntil(() => showHandler.calledOnce); await waitUntil(() => afterShowHandler.calledOnce); @@ -52,7 +52,7 @@ describe('', () => { el.addEventListener('sl-hide', hideHandler); el.addEventListener('sl-after-hide', afterHideHandler); - void el.hide(); + el.hide(); await waitUntil(() => hideHandler.calledOnce); await waitUntil(() => afterHideHandler.calledOnce); @@ -125,7 +125,7 @@ describe('', () => { }); el.addEventListener('sl-initial-focus', initialFocusHandler); - void el.show(); + el.show(); await waitUntil(() => initialFocusHandler.calledOnce); diff --git a/src/components/dialog/dialog.ts b/src/components/dialog/dialog.ts index 95067ce7..135c7c75 100644 --- a/src/components/dialog/dialog.ts +++ b/src/components/dialog/dialog.ts @@ -125,11 +125,11 @@ export default class SlDialog extends LitElement { const slRequestClose = emit(this, 'sl-request-close', { cancelable: true }); if (slRequestClose.defaultPrevented) { const animation = getAnimation(this, 'dialog.denyClose'); - void animateTo(this.panel, animation.keyframes, animation.options); + animateTo(this.panel, animation.keyframes, animation.options); return; } - void this.hide(); + this.hide(); } handleKeyDown(event: KeyboardEvent) { @@ -196,9 +196,7 @@ export default class SlDialog extends LitElement { // Restore focus to the original trigger const trigger = this.originalTrigger; if (typeof trigger?.focus === 'function') { - setTimeout(() => { - trigger.focus(); - }); + setTimeout(() => trigger.focus()); } emit(this, 'sl-after-hide'); @@ -206,6 +204,7 @@ export default class SlDialog extends LitElement { } render() { + /* eslint-disable lit-a11y/click-events-have-key-events */ return html`
-
+
`; + /* eslint-enable lit-a11y/click-events-have-key-events */ } } diff --git a/src/components/drawer/drawer.test.ts b/src/components/drawer/drawer.test.ts index ca2c4825..ad7ba4a5 100644 --- a/src/components/drawer/drawer.test.ts +++ b/src/components/drawer/drawer.test.ts @@ -32,7 +32,7 @@ describe('', () => { el.addEventListener('sl-show', showHandler); el.addEventListener('sl-after-show', afterShowHandler); - void el.show(); + el.show(); await waitUntil(() => showHandler.calledOnce); await waitUntil(() => afterShowHandler.calledOnce); @@ -52,7 +52,7 @@ describe('', () => { el.addEventListener('sl-hide', hideHandler); el.addEventListener('sl-after-hide', afterHideHandler); - void el.hide(); + el.hide(); await waitUntil(() => hideHandler.calledOnce); await waitUntil(() => afterHideHandler.calledOnce); @@ -125,7 +125,7 @@ describe('', () => { }); el.addEventListener('sl-initial-focus', initialFocusHandler); - void el.show(); + el.show(); await waitUntil(() => initialFocusHandler.calledOnce); diff --git a/src/components/drawer/drawer.ts b/src/components/drawer/drawer.ts index 636680ce..fb6e018e 100644 --- a/src/components/drawer/drawer.ts +++ b/src/components/drawer/drawer.ts @@ -142,11 +142,11 @@ export default class SlDrawer extends LitElement { const slRequestClose = emit(this, 'sl-request-close', { cancelable: true }); if (slRequestClose.defaultPrevented) { const animation = getAnimation(this, 'drawer.denyClose'); - void animateTo(this.panel, animation.keyframes, animation.options); + animateTo(this.panel, animation.keyframes, animation.options); return; } - void this.hide(); + this.hide(); } handleKeyDown(event: KeyboardEvent) { @@ -216,9 +216,7 @@ export default class SlDrawer extends LitElement { // Restore focus to the original trigger const trigger = this.originalTrigger; if (typeof trigger?.focus === 'function') { - setTimeout(() => { - trigger.focus(); - }); + setTimeout(() => trigger.focus()); } emit(this, 'sl-after-hide'); @@ -226,6 +224,7 @@ export default class SlDrawer extends LitElement { } render() { + /* eslint-disable lit-a11y/click-events-have-key-events */ return html`
-
+
`; + /* eslint-enable lit-a11y/click-events-have-key-events */ } } diff --git a/src/components/dropdown/dropdown.test.ts b/src/components/dropdown/dropdown.test.ts index c5d1bbad..30779482 100644 --- a/src/components/dropdown/dropdown.test.ts +++ b/src/components/dropdown/dropdown.test.ts @@ -52,7 +52,7 @@ describe('', () => { el.addEventListener('sl-show', showHandler); el.addEventListener('sl-after-show', afterShowHandler); - void el.show(); + el.show(); await waitUntil(() => showHandler.calledOnce); await waitUntil(() => afterShowHandler.calledOnce); @@ -79,7 +79,7 @@ describe('', () => { el.addEventListener('sl-hide', hideHandler); el.addEventListener('sl-after-hide', afterHideHandler); - void el.hide(); + el.hide(); await waitUntil(() => hideHandler.calledOnce); await waitUntil(() => afterHideHandler.calledOnce); diff --git a/src/components/dropdown/dropdown.ts b/src/components/dropdown/dropdown.ts index 7a5d7869..c4bc4f48 100644 --- a/src/components/dropdown/dropdown.ts +++ b/src/components/dropdown/dropdown.ts @@ -8,6 +8,7 @@ import type SlMenuItem from '~/components/menu-item/menu-item'; import type SlMenu from '~/components/menu/menu'; import { animateTo, stopAnimations } from '~/internal/animate'; import { emit, waitForEvent } from '~/internal/event'; +import { isTruthy } from '~/internal/is-truthy'; import { scrollIntoView } from '~/internal/scroll'; import { getTabbableBoundary } from '~/internal/tabbable'; import { watch } from '~/internal/watch'; @@ -99,7 +100,7 @@ export default class SlDropdown extends LitElement { } // Create the popover after render - void this.updateComplete.then(() => { + this.updateComplete.then(() => { this.popover = createPopper(this.trigger, this.positioner, { placement: this.placement, strategy: this.hoist ? 'fixed' : 'absolute', @@ -127,7 +128,7 @@ export default class SlDropdown extends LitElement { disconnectedCallback() { super.disconnectedCallback(); - void void this.hide(); + this.hide(); this.popover?.destroy(); } @@ -150,7 +151,7 @@ export default class SlDropdown extends LitElement { handleDocumentKeyDown(event: KeyboardEvent) { // Close when escape is pressed if (event.key === 'Escape') { - void this.hide(); + this.hide(); this.focusOnTrigger(); return; } @@ -160,7 +161,7 @@ export default class SlDropdown extends LitElement { // Tabbing within an open menu should close the dropdown and refocus the trigger if (this.open && document.activeElement?.tagName.toLowerCase() === 'sl-menu-item') { event.preventDefault(); - void this.hide(); + this.hide(); this.focusOnTrigger(); return; } @@ -176,10 +177,10 @@ export default class SlDropdown extends LitElement { : document.activeElement; if ( - typeof this.containingElement === 'undefined' || + !isTruthy(this.containingElement) || activeElement?.closest(this.containingElement.tagName.toLowerCase()) !== this.containingElement ) { - void this.hide(); + this.hide(); } }); } @@ -188,8 +189,8 @@ export default class SlDropdown extends LitElement { handleDocumentMouseDown(event: MouseEvent) { // Close when clicking outside of the containing element const path = event.composedPath(); - if (typeof this.containingElement !== 'undefined' && !path.includes(this.containingElement)) { - void this.hide(); + if (isTruthy(this.containingElement) && !path.includes(this.containingElement)) { + this.hide(); } } @@ -203,7 +204,7 @@ export default class SlDropdown extends LitElement { // Hide the dropdown when a menu item is selected if (!this.stayOpenOnSelect && target.tagName.toLowerCase() === 'sl-menu') { - void this.hide(); + this.hide(); this.focusOnTrigger(); } } @@ -213,7 +214,7 @@ export default class SlDropdown extends LitElement { @watch('placement') @watch('skidding') handlePopoverOptionsChange() { - void this.popover?.setOptions({ + this.popover?.setOptions({ placement: this.placement, strategy: this.hoist ? 'fixed' : 'absolute', modifiers: [ @@ -235,22 +236,22 @@ export default class SlDropdown extends LitElement { handleTriggerClick() { if (this.open) { - void this.hide(); + this.hide(); } else { - void this.show(); + this.show(); } } handleTriggerKeyDown(event: KeyboardEvent) { const menu = this.getMenu(); - const menuItems = typeof menu !== 'undefined' ? ([...menu.querySelectorAll('sl-menu-item')] as SlMenuItem[]) : []; + const menuItems = [...(menu?.querySelectorAll('sl-menu-item') ?? [])] as SlMenuItem[]; const firstMenuItem = menuItems[0]; const lastMenuItem = menuItems[menuItems.length - 1]; // Close when escape or tab is pressed if (event.key === 'Escape') { this.focusOnTrigger(); - void this.hide(); + this.hide(); return; } @@ -270,7 +271,7 @@ export default class SlDropdown extends LitElement { // Show the menu if it's not already open if (!this.open) { - void this.show(); + this.show(); } // Focus on a menu item @@ -355,7 +356,7 @@ export default class SlDropdown extends LitElement { return; } - void this.popover?.update(); + this.popover?.update(); } @watch('open', { waitUntilFirstUpdate: true }) @@ -375,7 +376,7 @@ export default class SlDropdown extends LitElement { document.addEventListener('mousedown', this.handleDocumentMouseDown); await stopAnimations(this); - void this.popover?.update(); + this.popover?.update(); this.panel.hidden = false; const { keyframes, options } = getAnimation(this, 'dropdown.show'); await animateTo(this.panel, keyframes, options); diff --git a/src/components/icon-button/icon-button.ts b/src/components/icon-button/icon-button.ts index c58fca56..eee81baa 100644 --- a/src/components/icon-button/icon-button.ts +++ b/src/components/icon-button/icon-button.ts @@ -4,6 +4,7 @@ import { classMap } from 'lit/directives/class-map.js'; import { ifDefined } from 'lit/directives/if-defined.js'; import styles from './icon-button.styles'; import '~/components/icon/icon'; +import { isTruthy } from '~/internal/is-truthy'; /** * @since 2.0 @@ -66,7 +67,7 @@ export default class SlIconButton extends LitElement { href=${ifDefined(this.href)} target=${ifDefined(this.target)} download=${ifDefined(this.download)} - rel=${ifDefined(typeof this.target !== 'undefined' ? 'noreferrer noopener' : undefined)} + rel=${ifDefined(isTruthy(this.target) ? 'noreferrer noopener' : undefined)} role="button" aria-disabled=${this.disabled ? 'true' : 'false'} aria-label="${this.label}" diff --git a/src/components/icon/icon.ts b/src/components/icon/icon.ts index d6018075..319bcf6e 100644 --- a/src/components/icon/icon.ts +++ b/src/components/icon/icon.ts @@ -47,7 +47,7 @@ export default class SlIcon extends LitElement { } firstUpdated() { - void this.setIcon(); + this.setIcon(); } disconnectedCallback() { @@ -65,7 +65,7 @@ export default class SlIcon extends LitElement { /** @internal Fetches the icon and redraws it. Used to handle library registrations. */ redraw() { - void this.setIcon(); + this.setIcon(); } @watch('name') @@ -76,7 +76,7 @@ export default class SlIcon extends LitElement { const url = this.getUrl(); if (typeof url !== 'undefined' && url.length > 0) { try { - const file = await requestIcon(url)!; + const file = await requestIcon(url); if (url !== this.getUrl()) { // If the url has changed while fetching the icon, ignore this request return; @@ -107,7 +107,7 @@ export default class SlIcon extends LitElement { } handleChange() { - void this.setIcon(); + this.setIcon(); } render() { diff --git a/src/components/image-comparer/image-comparer.ts b/src/components/image-comparer/image-comparer.ts index d260e37c..6e2a515e 100644 --- a/src/components/image-comparer/image-comparer.ts +++ b/src/components/image-comparer/image-comparer.ts @@ -3,7 +3,7 @@ import { customElement, property, query } from 'lit/decorators.js'; import { styleMap } from 'lit/directives/style-map.js'; import styles from './image-comparer.styles'; import '~/components/icon/icon'; -import { autoIncrement } from '~/internal/autoIncrement'; +import { autoIncrement } from '~/internal/auto-increment'; import { drag } from '~/internal/drag'; import { emit } from '~/internal/event'; import { clamp } from '~/internal/math'; diff --git a/src/components/include/include.test.ts b/src/components/include/include.test.ts index a47cde59..5b5ef2ac 100644 --- a/src/components/include/include.test.ts +++ b/src/components/include/include.test.ts @@ -1,4 +1,4 @@ -import { expect, fixture, html, waitUntil } from '@open-wc/testing'; +import { expect, fixture, html, waitUntil, aTimeout } from '@open-wc/testing'; import sinon from 'sinon'; import type SlInclude from './include'; @@ -20,12 +20,10 @@ const stubbedFetchResponse: Response = { clone: sinon.fake() }; -function delayResolve(resolveValue: string) { - return new Promise(resolve => { - setTimeout(() => { - resolve(resolveValue); - }); - }); +async function delayResolve(resolveValue: string) { + // Delay the fetch response to give time for the event listener to attach + await aTimeout(10); + return resolveValue; } describe('', () => { diff --git a/src/components/include/include.ts b/src/components/include/include.ts index cc7ab30c..bc8a6426 100644 --- a/src/components/include/include.ts +++ b/src/components/include/include.ts @@ -35,9 +35,7 @@ export default class SlInclude extends LitElement { executeScript(script: HTMLScriptElement) { // Create a copy of the script and swap it out so the browser executes it const newScript = document.createElement('script'); - [...script.attributes].forEach(attr => { - newScript.setAttribute(attr.name, attr.value); - }); + [...script.attributes].forEach(attr => newScript.setAttribute(attr.name, attr.value)); newScript.textContent = script.textContent; script.parentNode!.replaceChild(newScript, script); } @@ -65,9 +63,7 @@ export default class SlInclude extends LitElement { this.innerHTML = file.html; if (this.allowScripts) { - [...this.querySelectorAll('script')].forEach(script => { - this.executeScript(script); - }); + [...this.querySelectorAll('script')].forEach(script => this.executeScript(script)); } emit(this, 'sl-load'); diff --git a/src/components/input/input.ts b/src/components/input/input.ts index 9ad53203..193f7fe0 100644 --- a/src/components/input/input.ts +++ b/src/components/input/input.ts @@ -5,7 +5,7 @@ import { ifDefined } from 'lit/directives/if-defined.js'; import { live } from 'lit/directives/live.js'; import styles from './input.styles'; import '~/components/icon/icon'; -import { autoIncrement } from '~/internal/autoIncrement'; +import { autoIncrement } from '~/internal/auto-increment'; import { emit } from '~/internal/event'; import { FormSubmitController, getLabelledBy, renderFormControl } from '~/internal/form-control'; import { HasSlotController } from '~/internal/slot'; diff --git a/src/components/menu/menu.ts b/src/components/menu/menu.ts index 7f3d35c7..ed1cd13f 100644 --- a/src/components/menu/menu.ts +++ b/src/components/menu/menu.ts @@ -28,7 +28,7 @@ export default class SlMenu extends LitElement { @query('slot') defaultSlot: HTMLSlotElement; private typeToSelectString = ''; - private typeToSelectTimeout: NodeJS.Timeout; + private typeToSelectTimeout: number; firstUpdated() { this.setAttribute('role', 'menu'); @@ -79,14 +79,12 @@ export default class SlMenu extends LitElement { typeToSelect(key: string) { const items = this.getAllItems({ includeDisabled: false }); clearTimeout(this.typeToSelectTimeout); - this.typeToSelectTimeout = setTimeout(() => (this.typeToSelectString = ''), 750); + this.typeToSelectTimeout = window.setTimeout(() => (this.typeToSelectString = ''), 750); this.typeToSelectString += key.toLowerCase(); // Restore focus in browsers that don't support :focus-visible when using the keyboard if (!hasFocusVisible) { - items.forEach(item => { - item.classList.remove('sl-focus-invisible'); - }); + items.forEach(item => item.classList.remove('sl-focus-invisible')); } for (const item of items) { diff --git a/src/components/radio/radio.test.ts b/src/components/radio/radio.test.ts index 5228e084..1c1800fe 100644 --- a/src/components/radio/radio.test.ts +++ b/src/components/radio/radio.test.ts @@ -19,7 +19,7 @@ describe('', () => { it('should fire sl-change when clicked', async () => { const el = await fixture(html` `); - setTimeout(() => el.shadowRoot?.querySelector('input')?.click()); + setTimeout(() => el.shadowRoot!.querySelector('input')!.click()); const event = await oneEvent(el, 'sl-change'); expect(event.target).to.equal(el); expect(el.checked).to.be.true; @@ -29,9 +29,7 @@ describe('', () => { const el = await fixture(html` `); const input = el.shadowRoot!.querySelector('input')!; input.focus(); - setTimeout(() => { - void sendKeys({ press: ' ' }); - }); + setTimeout(() => sendKeys({ press: ' ' })); const event = await oneEvent(el, 'sl-change'); expect(event.target).to.equal(el); expect(el.checked).to.be.true; @@ -48,9 +46,7 @@ describe('', () => { const radio2 = radioGroup.querySelector('sl-radio#radio-2')!; const input1 = radio1.shadowRoot!.querySelector('input')!; input1.focus(); - setTimeout(() => { - void sendKeys({ press: 'ArrowRight' }); - }); + setTimeout(() => sendKeys({ press: 'ArrowRight' })); const event = await oneEvent(radio2, 'sl-change'); expect(event.target).to.equal(radio2); expect(radio2.checked).to.be.true; diff --git a/src/components/radio/radio.ts b/src/components/radio/radio.ts index 272c80dc..2dccd1cf 100644 --- a/src/components/radio/radio.ts +++ b/src/components/radio/radio.ts @@ -55,10 +55,9 @@ export default class SlRadio extends LitElement { @property({ type: Boolean, reflect: true }) invalid = false; firstUpdated() { - const radios = this.getAllRadios(); - const checkedRadio = radios.find(radio => radio.checked); - - setTimeout(() => { + this.updateComplete.then(() => { + const radios = this.getAllRadios(); + const checkedRadio = radios.find(radio => radio.checked); radios.forEach(radio => { radio.input.tabIndex = -1; }); diff --git a/src/components/range/range.ts b/src/components/range/range.ts index abdd61a7..4fc0c2c3 100644 --- a/src/components/range/range.ts +++ b/src/components/range/range.ts @@ -4,7 +4,7 @@ import { classMap } from 'lit/directives/class-map.js'; import { ifDefined } from 'lit/directives/if-defined.js'; import { live } from 'lit/directives/live.js'; import styles from './range.styles'; -import { autoIncrement } from '~/internal/autoIncrement'; +import { autoIncrement } from '~/internal/auto-increment'; import { emit } from '~/internal/event'; import { FormSubmitController, getLabelledBy, renderFormControl } from '~/internal/form-control'; import { HasSlotController } from '~/internal/slot'; @@ -88,9 +88,7 @@ export default class SlRange extends LitElement { connectedCallback() { super.connectedCallback(); - this.resizeObserver = new ResizeObserver(() => { - this.syncRange(); - }); + this.resizeObserver = new ResizeObserver(() => this.syncRange()); if (typeof this.value === 'undefined') { this.value = this.min; @@ -102,7 +100,7 @@ export default class SlRange extends LitElement { this.value = this.max; } - void this.updateComplete.then(() => { + this.updateComplete.then(() => { this.syncRange(); this.resizeObserver.observe(this.input); }); diff --git a/src/components/rating/rating.ts b/src/components/rating/rating.ts index c9746a4f..bc148ce7 100644 --- a/src/components/rating/rating.ts +++ b/src/components/rating/rating.ts @@ -203,9 +203,7 @@ export default class SlRating extends LitElement { return html` { - this.requestUpdate(); - }, nextInterval); + this.updateTimeout = window.setTimeout(() => this.requestUpdate(), nextInterval); } return html` `; diff --git a/src/components/resize-observer/resize-observer.ts b/src/components/resize-observer/resize-observer.ts index d82e11c3..3db70dd7 100644 --- a/src/components/resize-observer/resize-observer.ts +++ b/src/components/resize-observer/resize-observer.ts @@ -51,9 +51,7 @@ export default class SlResizeObserver extends LitElement { const elements = slot.assignedElements({ flatten: true }) as HTMLElement[]; // Unwatch previous elements - this.observedElements.forEach(el => { - this.resizeObserver.unobserve(el); - }); + this.observedElements.forEach(el => this.resizeObserver.unobserve(el)); this.observedElements = []; // Watch new elements diff --git a/src/components/select/select.ts b/src/components/select/select.ts index 6cd6685c..8b463ded 100644 --- a/src/components/select/select.ts +++ b/src/components/select/select.ts @@ -13,7 +13,7 @@ import type SlMenuItem from '~/components/menu-item/menu-item'; import type SlMenu from '~/components/menu/menu'; import type { MenuSelectEventDetail } from '~/components/menu/menu'; import '~/components/tag/tag'; -import { autoIncrement } from '~/internal/autoIncrement'; +import { autoIncrement } from '~/internal/auto-increment'; import { emit } from '~/internal/event'; import { FormSubmitController, getLabelledBy, renderFormControl } from '~/internal/form-control'; import { getTextContent, HasSlotController } from '~/internal/slot'; @@ -132,11 +132,9 @@ export default class SlSelect extends LitElement { connectedCallback() { super.connectedCallback(); this.handleMenuSlotChange = this.handleMenuSlotChange.bind(this); - this.resizeObserver = new ResizeObserver(() => { - this.resizeMenu(); - }); + this.resizeObserver = new ResizeObserver(() => this.resizeMenu()); - void this.updateComplete.then(() => { + this.updateComplete.then(() => { this.resizeObserver.observe(this); this.syncItemsFromValue(); }); @@ -163,7 +161,7 @@ export default class SlSelect extends LitElement { } getItemLabel(item: SlMenuItem) { - const slot = item.shadowRoot?.querySelector('slot:not([name])'); + const slot = item.shadowRoot!.querySelector('slot:not([name])'); return getTextContent(slot); } @@ -208,7 +206,7 @@ export default class SlSelect extends LitElement { @watch('disabled', { waitUntilFirstUpdate: true }) handleDisabledChange() { if (this.disabled && this.isOpen) { - void this.dropdown.hide(); + this.dropdown.hide(); } // Disabled form controls are always valid, so we need to recheck validity when the state changes @@ -237,7 +235,7 @@ export default class SlSelect extends LitElement { // Tabbing out of the control closes it if (event.key === 'Tab') { if (this.isOpen) { - void this.dropdown.hide(); + this.dropdown.hide(); } return; } @@ -248,7 +246,7 @@ export default class SlSelect extends LitElement { // Show the menu if it's not already open if (!this.isOpen) { - void this.dropdown.show(); + this.dropdown.show(); } // Focus on a menu item @@ -274,7 +272,7 @@ export default class SlSelect extends LitElement { if (!this.isOpen && event.key.length === 1) { event.stopPropagation(); event.preventDefault(); - void this.dropdown.show(); + this.dropdown.show(); this.menu.typeToSelect(event.key); } } @@ -331,9 +329,7 @@ export default class SlSelect extends LitElement { values.push(item.value); }); - await Promise.all(items.map(item => item.render)).then(() => { - this.syncItemsFromValue(); - }); + await Promise.all(items.map(item => item.render)).then(() => this.syncItemsFromValue()); } handleTagInteraction(event: KeyboardEvent | MouseEvent) { @@ -444,9 +440,7 @@ export default class SlSelect extends LitElement { helpText: this.helpText, hasHelpTextSlot, size: this.size, - onLabelClick: () => { - this.handleLabelClick(); - } + onLabelClick: () => this.handleLabelClick() }, html` { - this.handleResize(entries); - }); - void this.updateComplete.then(() => { - this.resizeObserver.observe(this); - }); + this.resizeObserver = new ResizeObserver(entries => this.handleResize(entries)); + this.updateComplete.then(() => this.resizeObserver.observe(this)); this.detectSize(); this.cachedPositionInPixels = this.percentageToPixels(this.position); diff --git a/src/components/switch/switch.test.ts b/src/components/switch/switch.test.ts index 6ec76a3a..010e18f1 100644 --- a/src/components/switch/switch.test.ts +++ b/src/components/switch/switch.test.ts @@ -18,7 +18,7 @@ describe('', () => { it('should fire sl-change when clicked', async () => { const el = await fixture(html` `); - setTimeout(() => el.shadowRoot?.querySelector('input')?.click()); + setTimeout(() => el.shadowRoot!.querySelector('input')!.click()); const event = await oneEvent(el, 'sl-change'); expect(event.target).to.equal(el); expect(el.checked).to.be.true; @@ -27,9 +27,7 @@ describe('', () => { it('should fire sl-change when toggled with spacebar', async () => { const el = await fixture(html` `); el.focus(); - setTimeout(() => { - void sendKeys({ press: ' ' }); - }); + setTimeout(() => sendKeys({ press: ' ' })); const event = await oneEvent(el, 'sl-change'); expect(event.target).to.equal(el); expect(el.checked).to.be.true; @@ -38,9 +36,7 @@ describe('', () => { it('should fire sl-change when toggled with the right arrow', async () => { const el = await fixture(html` `); el.focus(); - setTimeout(() => { - void sendKeys({ press: 'ArrowRight' }); - }); + setTimeout(() => sendKeys({ press: 'ArrowRight' })); const event = await oneEvent(el, 'sl-change'); expect(event.target).to.equal(el); expect(el.checked).to.be.true; @@ -49,9 +45,7 @@ describe('', () => { it('should fire sl-change when toggled with the left arrow', async () => { const el = await fixture(html` `); el.focus(); - setTimeout(() => { - void sendKeys({ press: 'ArrowLeft' }); - }); + setTimeout(() => sendKeys({ press: 'ArrowLeft' })); const event = await oneEvent(el, 'sl-change'); expect(event.target).to.equal(el); expect(el.checked).to.be.false; diff --git a/src/components/switch/switch.ts b/src/components/switch/switch.ts index 8c9cca47..f822a40e 100644 --- a/src/components/switch/switch.ts +++ b/src/components/switch/switch.ts @@ -4,7 +4,7 @@ import { classMap } from 'lit/directives/class-map.js'; import { ifDefined } from 'lit/directives/if-defined.js'; import { live } from 'lit/directives/live.js'; import styles from './switch.styles'; -import { autoIncrement } from '~/internal/autoIncrement'; +import { autoIncrement } from '~/internal/auto-increment'; import { emit } from '~/internal/event'; import { FormSubmitController } from '~/internal/form-control'; import { watch } from '~/internal/watch'; diff --git a/src/components/tab-group/tab-group.ts b/src/components/tab-group/tab-group.ts index 18531b22..9b6da3ac 100644 --- a/src/components/tab-group/tab-group.ts +++ b/src/components/tab-group/tab-group.ts @@ -78,9 +78,7 @@ export default class SlTabGroup extends LitElement { this.mutationObserver = new MutationObserver(mutations => { // Update aria labels when the DOM changes if (mutations.some(m => !['aria-labelledby', 'aria-controls'].includes(m.attributeName!))) { - setTimeout(() => { - this.setAriaLabels(); - }); + setTimeout(() => this.setAriaLabels()); } // Sync tabs when disabled states change @@ -89,7 +87,7 @@ export default class SlTabGroup extends LitElement { } }); - void this.updateComplete.then(() => { + this.updateComplete.then(() => { this.syncTabsAndPanels(); this.mutationObserver.observe(this, { attributes: true, childList: true, subtree: true }); this.resizeObserver.observe(this.nav); diff --git a/src/components/tab-panel/tab-panel.ts b/src/components/tab-panel/tab-panel.ts index d26c2bcd..66909fd0 100644 --- a/src/components/tab-panel/tab-panel.ts +++ b/src/components/tab-panel/tab-panel.ts @@ -1,7 +1,7 @@ import { LitElement, html } from 'lit'; import { customElement, property } from 'lit/decorators.js'; import styles from './tab-panel.styles'; -import { autoIncrement } from '~/internal/autoIncrement'; +import { autoIncrement } from '~/internal/auto-increment'; /** * @since 2.0 diff --git a/src/components/tab/tab.ts b/src/components/tab/tab.ts index 0021a9ad..7a05f198 100644 --- a/src/components/tab/tab.ts +++ b/src/components/tab/tab.ts @@ -3,7 +3,7 @@ import { customElement, property, query } from 'lit/decorators.js'; import { classMap } from 'lit/directives/class-map.js'; import styles from './tab.styles'; import '~/components/icon-button/icon-button'; -import { autoIncrement } from '~/internal/autoIncrement'; +import { autoIncrement } from '~/internal/auto-increment'; import { emit } from '~/internal/event'; import { LocalizeController } from '~/utilities/localize'; diff --git a/src/components/textarea/textarea.ts b/src/components/textarea/textarea.ts index 853c4af6..d868a02b 100644 --- a/src/components/textarea/textarea.ts +++ b/src/components/textarea/textarea.ts @@ -4,7 +4,7 @@ import { classMap } from 'lit/directives/class-map.js'; import { ifDefined } from 'lit/directives/if-defined.js'; import { live } from 'lit/directives/live.js'; import styles from './textarea.styles'; -import { autoIncrement } from '~/internal/autoIncrement'; +import { autoIncrement } from '~/internal/auto-increment'; import { emit } from '~/internal/event'; import { FormSubmitController, getLabelledBy, renderFormControl } from '~/internal/form-control'; import { HasSlotController } from '~/internal/slot'; @@ -116,11 +116,9 @@ export default class SlTextarea extends LitElement { connectedCallback() { super.connectedCallback(); - this.resizeObserver = new ResizeObserver(() => { - this.setTextareaHeight(); - }); + this.resizeObserver = new ResizeObserver(() => this.setTextareaHeight()); - void this.updateComplete.then(() => { + this.updateComplete.then(() => { this.setTextareaHeight(); this.resizeObserver.observe(this.input); }); @@ -151,7 +149,7 @@ export default class SlTextarea extends LitElement { } /** Gets or sets the textarea's scroll position. */ - scrollPosition(position?: { top?: number; left?: number }): void; + scrollPosition(position: { top?: number; left?: number }): void; scrollPosition(): { top: number; left: number }; scrollPosition(position?: { top?: number; left?: number }): { top: number; left: number } | undefined { if (typeof position !== 'undefined') { diff --git a/src/components/tooltip/tooltip.test.ts b/src/components/tooltip/tooltip.test.ts index 84328282..dd0650ca 100644 --- a/src/components/tooltip/tooltip.test.ts +++ b/src/components/tooltip/tooltip.test.ts @@ -37,7 +37,7 @@ describe('', () => { el.addEventListener('sl-show', showHandler); el.addEventListener('sl-after-show', afterShowHandler); - void el.show(); + el.show(); await waitUntil(() => showHandler.calledOnce); await waitUntil(() => afterShowHandler.calledOnce); @@ -59,7 +59,7 @@ describe('', () => { el.addEventListener('sl-hide', hideHandler); el.addEventListener('sl-after-hide', afterHideHandler); - void el.hide(); + el.hide(); await waitUntil(() => hideHandler.calledOnce); await waitUntil(() => afterHideHandler.calledOnce); diff --git a/src/components/tooltip/tooltip.ts b/src/components/tooltip/tooltip.ts index 5e64563c..0e03c725 100644 --- a/src/components/tooltip/tooltip.ts +++ b/src/components/tooltip/tooltip.ts @@ -39,7 +39,7 @@ export default class SlTooltip extends LitElement { private target: HTMLElement; private popover?: PopperInstance; - private hoverTimeout: NodeJS.Timeout; + private hoverTimeout: number; /** The tooltip's content. Alternatively, you can use the content slot. */ @property() content = ''; @@ -96,7 +96,7 @@ export default class SlTooltip extends LitElement { this.handleMouseOver = this.handleMouseOver.bind(this); this.handleMouseOut = this.handleMouseOut.bind(this); - void this.updateComplete.then(() => { + this.updateComplete.then(() => { this.addEventListener('blur', this.handleBlur, true); this.addEventListener('focus', this.handleFocus, true); this.addEventListener('click', this.handleClick); @@ -160,23 +160,23 @@ export default class SlTooltip extends LitElement { handleBlur() { if (this.hasTrigger('focus')) { - void this.hide(); + this.hide(); } } handleClick() { if (this.hasTrigger('click')) { if (this.open) { - void this.hide(); + this.hide(); } else { - void this.show(); + this.show(); } } } handleFocus() { if (this.hasTrigger('focus')) { - void this.show(); + this.show(); } } @@ -184,7 +184,7 @@ export default class SlTooltip extends LitElement { // Pressing escape when the target element has focus should dismiss the tooltip if (this.open && event.key === 'Escape') { event.stopPropagation(); - void this.hide(); + this.hide(); } } @@ -192,7 +192,7 @@ export default class SlTooltip extends LitElement { if (this.hasTrigger('hover')) { const delay = parseDuration(getComputedStyle(this).getPropertyValue('--show-delay')); clearTimeout(this.hoverTimeout); - this.hoverTimeout = setTimeout(() => void this.show(), delay); + this.hoverTimeout = window.setTimeout(() => void this.show(), delay); } } @@ -200,7 +200,7 @@ export default class SlTooltip extends LitElement { if (this.hasTrigger('hover')) { const delay = parseDuration(getComputedStyle(this).getPropertyValue('--hide-delay')); clearTimeout(this.hoverTimeout); - this.hoverTimeout = setTimeout(() => void this.hide(), delay); + this.hoverTimeout = window.setTimeout(() => void this.hide(), delay); } } @@ -268,14 +268,14 @@ export default class SlTooltip extends LitElement { @watch('content') handleContentChange() { if (this.open) { - void this.popover?.update(); + this.popover?.update(); } } @watch('disabled') handleDisabledChange() { if (this.disabled && this.open) { - void this.hide(); + this.hide(); } } @@ -285,7 +285,7 @@ export default class SlTooltip extends LitElement { } syncOptions() { - void this.popover?.setOptions({ + this.popover?.setOptions({ placement: this.placement, strategy: this.hoist ? 'fixed' : 'absolute', modifiers: [ diff --git a/src/internal/autoIncrement.ts b/src/internal/auto-increment.ts similarity index 100% rename from src/internal/autoIncrement.ts rename to src/internal/auto-increment.ts diff --git a/src/internal/form-control.ts b/src/internal/form-control.ts index 5ccafddd..8880c728 100644 --- a/src/internal/form-control.ts +++ b/src/internal/form-control.ts @@ -162,9 +162,7 @@ export function renderFormControl( class="form-control__label" for=${props.inputId} aria-hidden=${hasLabel ? 'false' : 'true'} - @click=${(event: MouseEvent) => { - props.onLabelClick?.(event); - }} + @click=${(event: MouseEvent) => props.onLabelClick?.(event)} > ${props.label} diff --git a/src/internal/is-truthy.ts b/src/internal/is-truthy.ts new file mode 100644 index 00000000..b9f70d6a --- /dev/null +++ b/src/internal/is-truthy.ts @@ -0,0 +1,4 @@ +export function isTruthy(obj: T | T[] | undefined | null | '' | [] | 0 | false): obj is T | T[] { + // eslint-disable-next-line no-restricted-syntax -- we do intentionally want the type coercion behavior + return Boolean(obj); +} diff --git a/src/internal/number.ts b/src/internal/number.ts index ffd18098..1b574547 100644 --- a/src/internal/number.ts +++ b/src/internal/number.ts @@ -6,7 +6,7 @@ const numberFormatter = new Intl.NumberFormat(); export function formatBytes(bytes: number, options: FormatBytesOptions) { options = { unit: 'bytes', - formatter: (number: number) => numberFormatter.format(number), + formatter: (num: number) => numberFormatter.format(num), ...options }; diff --git a/src/internal/tabbable.ts b/src/internal/tabbable.ts index 0759e23a..637abce2 100644 --- a/src/internal/tabbable.ts +++ b/src/internal/tabbable.ts @@ -67,9 +67,7 @@ export function getTabbableBoundary(root: HTMLElement | ShadowRoot) { } } - [...el.querySelectorAll('*')].forEach((e: HTMLElement) => { - walk(e); - }); + [...el.querySelectorAll('*')].forEach((e: HTMLElement) => walk(e)); } // Collect all elements including the root diff --git a/src/internal/watch.ts b/src/internal/watch.ts index 630c9e5a..bc18fa80 100644 --- a/src/internal/watch.ts +++ b/src/internal/watch.ts @@ -13,11 +13,11 @@ // } import type { LitElement } from 'lit'; -import type { NonUndefined } from 'utility-types'; type UpdateHandler = (prev?: unknown, next?: unknown) => void; -// Mostly copied from utility-types' FunctionKeys type but this uses a specific function signature +type NonUndefined = A extends undefined ? never : A; + type UpdateHandlerFunctionKeys = { [K in keyof T]-?: NonUndefined extends UpdateHandler ? K : never; }[keyof T];