Skip to content

Commit

Permalink
chore(typescript): migration to typescript (downshift-js#1268)
Browse files Browse the repository at this point in the history
* fix rollup

* update tsconfig

* move eslint config from package.json

* fix jest config setup file

* refactor useSelect utils

* move eslint back to package

* move eslintignore back to package

* fix lint

* fix build

* update utils tests
  • Loading branch information
silviuaavram authored Aug 2, 2021
1 parent 1c76980 commit dd86e00
Show file tree
Hide file tree
Showing 12 changed files with 181 additions and 135 deletions.
3 changes: 2 additions & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ module.exports = Object.assign(jestConfig, {
'.macro.js$',
'<rootDir>/src/stateChangeTypes.js',
],
setupFilesAfterEnv: ['<rootDir>/test/setup.js'],
setupFilesAfterEnv: ['<rootDir>/test/setup.ts'],
moduleFileExtensions: ['ts', 'js', 'tsx', 'jsx'],
})
42 changes: 21 additions & 21 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
"author": "Kent C. Dodds <[email protected]> (http://kentcdodds.com/)",
"license": "MIT",
"peerDependencies": {
"react": ">=16.12.0"
"react": ">=17.0.2"
},
"dependencies": {
"@babel/runtime": "^7.14.8",
Expand All @@ -74,7 +74,8 @@
"devDependencies": {
"@babel/helpers": "^7.14.8",
"@rollup/plugin-babel": "^5.3.0",
"@rollup/plugin-commonjs": "^19.0.2",
"@rollup/plugin-commonjs": "^20.0.0",
"@rollup/plugin-typescript": "^8.2.5",
"@testing-library/cypress": "^8.0.0",
"@testing-library/dom": "^8.1.0",
"@testing-library/jest-dom": "^5.14.1",
Expand All @@ -84,15 +85,18 @@
"@testing-library/user-event": "^13.2.1",
"@types/jest": "^26.0.24",
"@types/react": "^17.0.15",
"@typescript-eslint/eslint-plugin": "^4.28.5",
"@typescript-eslint/parser": "^4.28.5",
"babel-plugin-macros": "^3.1.0",
"babel-plugin-no-side-effect-class-properties": "0.0.7",
"babel-preset-react-native": "^4.0.1",
"buble": "^0.20.0",
"cpy-cli": "^3.1.1",
"cross-env": "^7.0.3",
"cypress": "^8.0.0",
"cypress": "^8.1.0",
"docz": "^2.3.1",
"docz-theme-default": "^1.2.0",
"eslint": "^7.32.0",
"eslint-plugin-cypress": "^2.11.3",
"eslint-plugin-react": "7.24.0",
"flow-bin": "^0.156.0",
Expand All @@ -101,7 +105,7 @@
"kcd-scripts": "^11.2.0",
"npm-run-all": "^4.1.5",
"preact": "^10.5.14",
"react": "^16.13.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-native": "^0.64.2",
"react-test-renderer": "^17.0.2",
Expand All @@ -110,30 +114,26 @@
"typescript": "^4.3.5"
},
"eslintConfig": {
"settings": {
"import/resolver": {
"node": {
"extensions": [
".js",
".jsx",
".ts",
".tsx"
]
}
}
},
"extends": "./node_modules/kcd-scripts/eslint.js",
"rules": {
"eqeqeq": "off",
"import/no-useless-path-segments": "off",
"import/no-unassigned-import": "off",
"max-lines": "off",
"max-lines-per-function": "off",
"no-eq-null": "off",
"react/jsx-indent": "off",
"react/prop-types": "off",
"max-lines-per-function": "off",
"jsx-a11y/label-has-for": "off",
"jsx-a11y/label-has-associated-control": "off",
"jsx-a11y/autocomplete-valid": "off",
"complexity": [
"error",
12
],
"no-unsafe-optional-chaining": "off",
"no-loss-of-precision": "off",
"no-unreachable-loop": "off",
"no-useless-backreference": "off",
"default-case-last": "off",
"no-nonoctal-decimal-escape": "off",
"id-denylist": "off",
"testing-library/prefer-user-event": "off",
"testing-library/no-node-access": "off",
"testing-library/no-container": "off",
Expand Down
16 changes: 15 additions & 1 deletion rollup.config.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
const commonjs = require('@rollup/plugin-commonjs')
const {babel} = require('@rollup/plugin-babel')
const config = require('kcd-scripts/dist/config/rollup.config.js')
const typescript = require('@rollup/plugin-typescript')
const config = require('kcd-scripts/dist/config/rollup.config')

const babelPluginIndex = config.plugins.findIndex(
plugin => plugin.name === 'babel',
)
const typescriptPluginIndex = config.plugins.findIndex(
plugin => plugin.name === 'typescript',
)
const cjsPluginIndex = config.plugins.findIndex(
plugin => plugin.name === 'commonjs',
)
Expand All @@ -14,4 +19,13 @@ config.plugins[babelPluginIndex] = babel({
config.plugins[cjsPluginIndex] = commonjs({
include: 'node_modules/**',
})

if (typescriptPluginIndex === -1) {
config.plugins.push(typescript({tsconfig: 'tsconfig.json'}))
} else {
config.plugins[typescriptPluginIndex] = typescript({
tsconfig: 'tsconfig.json',
})
}

module.exports = config
64 changes: 0 additions & 64 deletions src/hooks/useSelect/__tests__/utils.test.js

This file was deleted.

64 changes: 64 additions & 0 deletions src/hooks/useSelect/__tests__/utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import {getItemIndexByCharacterKey} from '../utils'
import reducer from '../reducer'

describe('getItemIndexByCharacterKey', () => {
const items = ['a', 'b', 'aba', 'aab', 'bab']
const itemToString = jest.fn().mockImplementation(item => item)
const getItemNodeFromIndex = jest.fn()

test('returns to check from start if from highlightedIndex does not find anything', () => {
const index = getItemIndexByCharacterKey({
keysSoFar: 'a',
highlightedIndex: 3,
items,
itemToString,
getItemNodeFromIndex,
})
expect(index).toBe(0)
})

test('checks from highlightedIndex position inclusively if there is more than one key', () => {
const index = getItemIndexByCharacterKey({
keysSoFar: 'aba',
highlightedIndex: 2,
items,
itemToString,
getItemNodeFromIndex,
})
expect(index).toBe(2)
})

test('checks from highlightedIndex position exclusively if there is only one key', () => {
const index = getItemIndexByCharacterKey({
keysSoFar: 'a',
highlightedIndex: 2,
items,
itemToString,
getItemNodeFromIndex,
})
expect(index).toBe(3)
})

test('skips disabled item and moves to next', () => {
const keysSoFar = 'b'
const highlightedIndex = 0

expect(
getItemIndexByCharacterKey({
keysSoFar,
highlightedIndex,
items,
itemToString,
getItemNodeFromIndex: jest
.fn()
.mockImplementation(index => ({hasAttribute: () => index === 1})),
}),
).toEqual(4)
})
})

test('reducer throws error if called without proper action type', () => {
expect(() => {
reducer({}, {type: 'super-bogus'})
}).toThrowError('Reducer called without proper action type.')
})
30 changes: 16 additions & 14 deletions src/hooks/useSelect/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,15 @@ export default function downshiftSelectReducer(state, action) {
{
const lowercasedKey = action.key
const inputValue = `${state.inputValue}${lowercasedKey}`
const itemIndex = getItemIndexByCharacterKey(
inputValue,
state.selectedItem ? props.items.indexOf(state.selectedItem) : -1,
props.items,
props.itemToString,
action.getItemNodeFromIndex,
)
const itemIndex = getItemIndexByCharacterKey({
keysSoFar: inputValue,
highlightedIndex: state.selectedItem
? props.items.indexOf(state.selectedItem)
: -1,
items: props.items,
itemToString: props.itemToString,
getItemNodeFromIndex: action.getItemNodeFromIndex,
})

changes = {
inputValue,
Expand Down Expand Up @@ -116,13 +118,13 @@ export default function downshiftSelectReducer(state, action) {
{
const lowercasedKey = action.key
const inputValue = `${state.inputValue}${lowercasedKey}`
const highlightedIndex = getItemIndexByCharacterKey(
inputValue,
state.highlightedIndex,
props.items,
props.itemToString,
action.getItemNodeFromIndex,
)
const highlightedIndex = getItemIndexByCharacterKey({
keysSoFar: inputValue,
highlightedIndex: state.highlightedIndex,
items: props.items,
itemToString: props.itemToString,
getItemNodeFromIndex: action.getItemNodeFromIndex,
})

changes = {
inputValue,
Expand Down
9 changes: 9 additions & 0 deletions src/hooks/useSelect/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/* Internal Types */

export interface GetItemIndexByCharacterKeyOptions<Item> {
keysSoFar: string
highlightedIndex: number
items: Item[]
itemToString(item: Item | null): string
getItemNodeFromIndex(index: number): HTMLElement | undefined
}
29 changes: 19 additions & 10 deletions src/hooks/useSelect/utils.js → src/hooks/useSelect/utils.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,31 @@
import PropTypes from 'prop-types'
import {defaultProps as commonDefaultProps} from '../utils'
import {noop} from '../../utils'
import {A11yStatusMessageOptions} from '../../types'
import {GetItemIndexByCharacterKeyOptions} from './types'

function getItemIndexByCharacterKey(
export function getItemIndexByCharacterKey<Item>({
keysSoFar,
highlightedIndex,
items,
itemToString,
getItemNodeFromIndex,
) {
}: GetItemIndexByCharacterKeyOptions<Item>) {
const lowerCasedKeysSoFar = keysSoFar.toLowerCase()

for (let index = 0; index < items.length; index++) {
const offsetIndex = (index + highlightedIndex + 1) % items.length
const item = items[offsetIndex]

if (
itemToString(items[offsetIndex])
item !== undefined &&
itemToString(item)
.toLowerCase()
.startsWith(lowerCasedKeysSoFar)
) {
const element = getItemNodeFromIndex(offsetIndex)

if (!(element && element.hasAttribute('disabled'))) {
if (!element?.hasAttribute('disabled')) {
return offsetIndex
}
}
Expand Down Expand Up @@ -74,7 +78,11 @@ const propTypes = {
* @param {Object} param the downshift state and other relevant properties
* @return {String} the a11y status message
*/
function getA11yStatusMessage({isOpen, resultCount, previousResultCount}) {
function getA11yStatusMessage<Item>({
isOpen,
resultCount,
previousResultCount,
}: A11yStatusMessageOptions<Item>): string {
if (!isOpen) {
return ''
}
Expand All @@ -92,18 +100,19 @@ function getA11yStatusMessage({isOpen, resultCount, previousResultCount}) {
return ''
}

const defaultProps = {
export const defaultProps = {
...commonDefaultProps,
getA11yStatusMessage,
}

// eslint-disable-next-line import/no-mutable-exports
let validatePropTypes = noop
export let validatePropTypes = noop as (
options: unknown,
caller: Function,
) => void
/* istanbul ignore next */
if (process.env.NODE_ENV !== 'production') {
validatePropTypes = (options, caller) => {
validatePropTypes = (options: unknown, caller: Function): void => {
PropTypes.checkPropTypes(propTypes, options, 'prop', caller.name)
}
}

export {getItemIndexByCharacterKey, defaultProps, validatePropTypes}
Loading

0 comments on commit dd86e00

Please sign in to comment.