Skip to content

Commit accb6cc

Browse files
feat: migrate some files to typescript (#848)
Co-authored-by: eps1lon <[email protected]>
1 parent f753c8a commit accb6cc

9 files changed

+107
-39
lines changed

package.json

+6-3
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
"node": ">=10"
2525
},
2626
"scripts": {
27-
"build": "kcd-scripts build --ignore \"**/__tests__/**,**/__node_tests__/**,**/__mocks__/**\" && kcd-scripts build --bundle --no-clean",
27+
"build": "kcd-scripts build --no-ts-defs --ignore \"**/__tests__/**,**/__node_tests__/**,**/__mocks__/**\" && kcd-scripts build --no-ts-defs --bundle --no-clean",
2828
"lint": "kcd-scripts lint",
2929
"setup": "npm install && npm run validate -s",
3030
"test": "kcd-scripts test",
@@ -53,11 +53,14 @@
5353
"jest-serializer-ansi": "^1.0.3",
5454
"jest-watch-select-projects": "^2.0.0",
5555
"jsdom": "^16.4.0",
56-
"kcd-scripts": "^7.5.1",
56+
"kcd-scripts": "^7.5.3",
5757
"typescript": "^4.1.2"
5858
},
5959
"eslintConfig": {
60-
"extends": "./node_modules/kcd-scripts/eslint.js",
60+
"extends": [
61+
"./node_modules/kcd-scripts/eslint.js",
62+
"plugin:import/typescript"
63+
],
6164
"rules": {
6265
"import/prefer-default-export": "off",
6366
"import/no-unassigned-import": "off",

src/config.js renamed to src/config.ts

+11-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
1+
import {Config, ConfigFn} from '../types/config'
12
import {prettyDOM} from './pretty-dom'
23

4+
type Callback<T> = () => T
5+
interface InternalConfig extends Config {
6+
_disableExpensiveErrorDiagnostics: boolean
7+
}
8+
39
// It would be cleaner for this to live inside './queries', but
410
// other parts of the code assume that all exports from
511
// './queries' are query functions.
6-
let config = {
12+
let config: InternalConfig = {
713
testIdAttribute: 'data-testid',
814
asyncUtilTimeout: 1000,
915
// this is to support React's async `act` function.
@@ -36,7 +42,9 @@ let config = {
3642
}
3743

3844
export const DEFAULT_IGNORE_TAGS = 'script, style'
39-
export function runWithExpensiveErrorDiagnosticsDisabled(callback) {
45+
export function runWithExpensiveErrorDiagnosticsDisabled<T>(
46+
callback: Callback<T>,
47+
) {
4048
try {
4149
config._disableExpensiveErrorDiagnostics = true
4250
return callback()
@@ -45,7 +53,7 @@ export function runWithExpensiveErrorDiagnosticsDisabled(callback) {
4553
}
4654
}
4755

48-
export function configure(newConfig) {
56+
export function configure(newConfig: Partial<Config> | ConfigFn) {
4957
if (typeof newConfig === 'function') {
5058
// Pass the existing config out to the provided function
5159
// and accept a delta in return

src/events.js

+9-6
Original file line numberDiff line numberDiff line change
@@ -71,12 +71,15 @@ function createEvent(
7171
/* istanbul ignore if */
7272
if (typeof window.DataTransfer === 'function') {
7373
Object.defineProperty(event, dataTransferKey, {
74-
value: Object
75-
.getOwnPropertyNames(dataTransferValue)
76-
.reduce((acc, propName) => {
77-
Object.defineProperty(acc, propName, {value: dataTransferValue[propName]});
78-
return acc;
79-
}, new window.DataTransfer())
74+
value: Object.getOwnPropertyNames(dataTransferValue).reduce(
75+
(acc, propName) => {
76+
Object.defineProperty(acc, propName, {
77+
value: dataTransferValue[propName],
78+
})
79+
return acc
80+
},
81+
new window.DataTransfer(),
82+
),
8083
})
8184
} else {
8285
Object.defineProperty(event, dataTransferKey, {

src/label-helpers.js renamed to src/label-helpers.ts

+22-15
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ const labelledNodeNames = [
1010
'input',
1111
]
1212

13-
function getTextContent(node) {
13+
function getTextContent(
14+
node: Node | Element | HTMLInputElement,
15+
): string | null {
1416
if (labelledNodeNames.includes(node.nodeName.toLowerCase())) {
1517
return ''
1618
}
@@ -22,37 +24,43 @@ function getTextContent(node) {
2224
.join('')
2325
}
2426

25-
function getLabelContent(node) {
26-
let textContent
27-
if (node.tagName.toLowerCase() === 'label') {
28-
textContent = getTextContent(node)
27+
function getLabelContent(element: Element): string | null {
28+
let textContent: string | null
29+
if (element.tagName.toLowerCase() === 'label') {
30+
textContent = getTextContent(element)
2931
} else {
30-
textContent = node.value || node.textContent
32+
textContent = (element as HTMLInputElement).value || element.textContent
3133
}
3234
return textContent
3335
}
3436

3537
// Based on https://github.com/eps1lon/dom-accessibility-api/pull/352
36-
function getRealLabels(element) {
37-
if (element.labels !== undefined) return element.labels ?? []
38+
function getRealLabels(element: Element) {
39+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- types are not aware of older browsers that don't implement `labels`
40+
if ((element as HTMLInputElement).labels !== undefined) {
41+
return (element as HTMLInputElement).labels ?? []
42+
}
3843

3944
if (!isLabelable(element)) return []
4045

4146
const labels = element.ownerDocument.querySelectorAll('label')
4247
return Array.from(labels).filter(label => label.control === element)
4348
}
4449

45-
function isLabelable(element) {
50+
function isLabelable(element: Element) {
4651
return (
47-
element.tagName.match(/BUTTON|METER|OUTPUT|PROGRESS|SELECT|TEXTAREA/) ||
52+
/BUTTON|METER|OUTPUT|PROGRESS|SELECT|TEXTAREA/.test(element.tagName) ||
4853
(element.tagName === 'INPUT' && element.getAttribute('type') !== 'hidden')
4954
)
5055
}
5156

52-
function getLabels(container, element, {selector = '*'} = {}) {
53-
const labelsId = element.getAttribute('aria-labelledby')
54-
? element.getAttribute('aria-labelledby').split(' ')
55-
: []
57+
function getLabels(
58+
container: Element,
59+
element: Element,
60+
{selector = '*'} = {},
61+
) {
62+
const ariaLabelledBy = element.getAttribute('aria-labelledby')
63+
const labelsId = ariaLabelledBy ? ariaLabelledBy.split(' ') : []
5664
return labelsId.length
5765
? labelsId.map(labelId => {
5866
const labellingElement = container.querySelector(`[id="${labelId}"]`)
@@ -67,7 +75,6 @@ function getLabels(container, element, {selector = '*'} = {}) {
6775
const labelledFormControl = Array.from(
6876
label.querySelectorAll(formControlSelector),
6977
).filter(formControlElement => formControlElement.matches(selector))[0]
70-
7178
return {content: textToMatch, formControl: labelledFormControl}
7279
})
7380
}

src/matches.js renamed to src/matches.ts

+37-7
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,36 @@
1-
function assertNotNullOrUndefined(matcher) {
2-
if (matcher == null) {
1+
import {
2+
Matcher,
3+
NormalizerFn,
4+
NormalizerOptions,
5+
DefaultNormalizerOptions,
6+
} from '../types'
7+
8+
type Nullish<T> = T | null | undefined
9+
10+
function assertNotNullOrUndefined<T>(
11+
matcher: T,
12+
): asserts matcher is NonNullable<T> {
13+
if (matcher === null || matcher === undefined) {
314
throw new Error(
15+
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions -- implicitly converting `T` to `string`
416
`It looks like ${matcher} was passed instead of a matcher. Did you do something like getByText(${matcher})?`,
517
)
618
}
719
}
820

9-
function fuzzyMatches(textToMatch, node, matcher, normalizer) {
21+
function fuzzyMatches(
22+
textToMatch: Nullish<string>,
23+
node: Nullish<Element>,
24+
matcher: Nullish<Matcher>,
25+
normalizer: NormalizerFn,
26+
) {
1027
if (typeof textToMatch !== 'string') {
1128
return false
1229
}
13-
1430
assertNotNullOrUndefined(matcher)
1531

1632
const normalizedText = normalizer(textToMatch)
33+
1734
if (typeof matcher === 'string') {
1835
return normalizedText.toLowerCase().includes(matcher.toLowerCase())
1936
} else if (typeof matcher === 'function') {
@@ -23,7 +40,12 @@ function fuzzyMatches(textToMatch, node, matcher, normalizer) {
2340
}
2441
}
2542

26-
function matches(textToMatch, node, matcher, normalizer) {
43+
function matches(
44+
textToMatch: Nullish<string>,
45+
node: Nullish<Element>,
46+
matcher: Nullish<Matcher>,
47+
normalizer: NormalizerFn,
48+
) {
2749
if (typeof textToMatch !== 'string') {
2850
return false
2951
}
@@ -40,7 +62,10 @@ function matches(textToMatch, node, matcher, normalizer) {
4062
}
4163
}
4264

43-
function getDefaultNormalizer({trim = true, collapseWhitespace = true} = {}) {
65+
function getDefaultNormalizer({
66+
trim = true,
67+
collapseWhitespace = true,
68+
}: DefaultNormalizerOptions = {}): NormalizerFn {
4469
return text => {
4570
let normalizedText = text
4671
normalizedText = trim ? normalizedText.trim() : normalizedText
@@ -60,7 +85,12 @@ function getDefaultNormalizer({trim = true, collapseWhitespace = true} = {}) {
6085
* @param {Function|undefined} normalizer The user-specified normalizer
6186
* @returns {Function} A normalizer
6287
*/
63-
function makeNormalizer({trim, collapseWhitespace, normalizer}) {
88+
89+
function makeNormalizer({
90+
trim,
91+
collapseWhitespace,
92+
normalizer,
93+
}: NormalizerOptions) {
6494
if (normalizer) {
6595
// User has specified a custom normalizer
6696
if (

tsconfig.json

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"extends": "./node_modules/kcd-scripts/shared-tsconfig.json",
3+
"compilerOptions": {
4+
"allowJs": true
5+
},
6+
"include": ["./src", "./types"]
7+
}

types/config.d.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
export interface Config {
22
testIdAttribute: string
3+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
34
asyncWrapper(cb: (...args: any[]) => any): Promise<any>
5+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
46
eventWrapper(cb: (...args: any[]) => any): void
57
asyncUtilTimeout: number
68
computedStyleSupportsPseudoElements: boolean
79
defaultHidden: boolean
810
showOriginalStackTrace: boolean
911
throwSuggestions: boolean
10-
getElementError: (message: string, container: HTMLElement) => Error
12+
getElementError: (message: string | null, container: Element) => Error
1113
}
1214

1315
export interface ConfigFn {

types/matches.d.ts

+11-2
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,23 @@
11
import {ARIARole} from 'aria-query'
22

3-
export type MatcherFunction = (content: string, element: HTMLElement) => boolean
4-
export type Matcher = MatcherFunction | {}
3+
type Nullish<T> = T | null | undefined
4+
5+
export type MatcherFunction = (
6+
content: string,
7+
element: Nullish<Element>,
8+
) => boolean
9+
export type Matcher = MatcherFunction | RegExp | string
510

611
// Get autocomplete for ARIARole union types, while still supporting another string
712
// Ref: https://github.com/microsoft/TypeScript/issues/29729#issuecomment-505826972
813
export type ByRoleMatcher = ARIARole | MatcherFunction | {}
914

1015
export type NormalizerFn = (text: string) => string
1116

17+
export interface NormalizerOptions extends DefaultNormalizerOptions {
18+
normalizer?: NormalizerFn
19+
}
20+
1221
export interface MatcherOptions {
1322
exact?: boolean
1423
/** Use normalizer with getDefaultNormalizer instead */

types/tsconfig.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
11
{
2-
"extends": "../node_modules/kcd-scripts/shared-tsconfig.json",
3-
"include": ["."]
2+
"extends": "../tsconfig.json"
43
}

0 commit comments

Comments
 (0)