Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Button][base] Prevent too many ref updates (#33882)
- Loading branch information
1 parent
5a9b953
commit a0c5385
Showing
5 changed files
with
190 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
147 changes: 147 additions & 0 deletions
147
packages/mui-base/src/ListboxUnstyled/useControllableReducer.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
import { expect } from 'chai'; | ||
import * as React from 'react'; | ||
import { spy } from 'sinon'; | ||
import { createRenderer } from 'test/utils'; | ||
import useControllableReducer from './useControllableReducer'; | ||
import { | ||
ActionTypes, | ||
ListboxAction, | ||
ListboxState, | ||
UseListboxPropsWithDefaults, | ||
} from './useListbox.types'; | ||
|
||
describe('useControllableReducer', () => { | ||
const { render } = createRenderer(); | ||
|
||
describe('dispatch', () => { | ||
it('calls the provided internal reducer', () => { | ||
// eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
const reducer = spy((state: ListboxState<string>, action: ListboxAction<string>) => { | ||
return state; | ||
}); | ||
|
||
const actionToDispatch = { type: ActionTypes.setValue as const, value: 'b' }; | ||
const TestComponent = () => { | ||
const props: UseListboxPropsWithDefaults<string> = { | ||
options: ['a', 'b', 'c'], | ||
defaultValue: 'a', | ||
isOptionDisabled: () => false, | ||
disableListWrap: false, | ||
disabledItemsFocusable: false, | ||
optionComparer: (a, b) => a === b, | ||
optionStringifier: (option) => option, | ||
multiple: false, | ||
}; | ||
const [, dispatch] = useControllableReducer(reducer, undefined, props); | ||
React.useEffect(() => dispatch(actionToDispatch), [dispatch]); | ||
return null; | ||
}; | ||
|
||
render(<TestComponent />); | ||
expect(reducer.getCalls()[0].args[1]).to.equal(actionToDispatch); | ||
}); | ||
|
||
it('calls the provided external reducer', () => { | ||
// eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
const internalReducer = spy((state: ListboxState<string>, action: ListboxAction<string>) => { | ||
return state; | ||
}); | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
const externalReducer = spy((state: ListboxState<string>, action: ListboxAction<string>) => { | ||
return state; | ||
}); | ||
|
||
const actionToDispatch = { type: ActionTypes.setValue as const, value: 'b' }; | ||
const TestComponent = () => { | ||
const props: UseListboxPropsWithDefaults<string> = { | ||
options: ['a', 'b', 'c'], | ||
defaultValue: 'a', | ||
isOptionDisabled: () => false, | ||
disableListWrap: false, | ||
disabledItemsFocusable: false, | ||
optionComparer: (a, b) => a === b, | ||
optionStringifier: (option) => option, | ||
multiple: false, | ||
}; | ||
const [, dispatch] = useControllableReducer(internalReducer, externalReducer, props); | ||
React.useEffect(() => dispatch(actionToDispatch), [dispatch]); | ||
return null; | ||
}; | ||
|
||
render(<TestComponent />); | ||
expect(internalReducer.notCalled).to.equal(true); | ||
expect(externalReducer.getCalls()[0].args[1]).to.equal(actionToDispatch); | ||
}); | ||
|
||
it('calls onChange when the reducer returns a modified selected value', () => { | ||
const reducer = spy((state: ListboxState<string>) => { | ||
return { | ||
...state, | ||
selectedValue: 'b', | ||
}; | ||
}); | ||
|
||
const actionToDispatch = { type: ActionTypes.setValue as const, value: 'b' }; | ||
const handleChange = spy(); | ||
const handleHighlightChange = spy(); | ||
|
||
const TestComponent = () => { | ||
const props: UseListboxPropsWithDefaults<string> = { | ||
options: ['a', 'b', 'c'], | ||
defaultValue: 'a', | ||
isOptionDisabled: () => false, | ||
disableListWrap: false, | ||
disabledItemsFocusable: false, | ||
optionComparer: (a, b) => a === b, | ||
optionStringifier: (option) => option, | ||
multiple: false, | ||
onChange: handleChange, | ||
onHighlightChange: handleHighlightChange, | ||
}; | ||
const [, dispatch] = useControllableReducer(reducer, undefined, props); | ||
React.useEffect(() => dispatch(actionToDispatch), [dispatch]); | ||
return null; | ||
}; | ||
|
||
render(<TestComponent />); | ||
expect(handleChange.getCalls()[0].args[0]).to.equal('b'); | ||
expect(handleHighlightChange.notCalled).to.equal(true); | ||
}); | ||
|
||
it('calls onHighlightChange when the reducer returns a modified highlighted value', () => { | ||
const reducer = spy((state: ListboxState<string>) => { | ||
return { | ||
...state, | ||
highlightedValue: 'b', | ||
}; | ||
}); | ||
|
||
const actionToDispatch = { type: ActionTypes.setHighlight as const, highlight: 'b' }; | ||
const handleChange = spy(); | ||
const handleHighlightChange = spy(); | ||
|
||
const TestComponent = () => { | ||
const props: UseListboxPropsWithDefaults<string> = { | ||
options: ['a', 'b', 'c'], | ||
defaultValue: 'a', | ||
isOptionDisabled: () => false, | ||
disableListWrap: false, | ||
disabledItemsFocusable: false, | ||
optionComparer: (a, b) => a === b, | ||
optionStringifier: (option) => option, | ||
multiple: false, | ||
onChange: handleChange, | ||
onHighlightChange: handleHighlightChange, | ||
}; | ||
const [, dispatch] = useControllableReducer(reducer, undefined, props); | ||
React.useEffect(() => dispatch(actionToDispatch), [dispatch]); | ||
return null; | ||
}; | ||
|
||
render(<TestComponent />); | ||
expect(handleHighlightChange.getCalls()[0].args[0]).to.equal('b'); | ||
expect(handleChange.notCalled).to.equal(true); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters