Skip to content

Commit

Permalink
fix(searchbox): ignore composition events with option (#6009)
Browse files Browse the repository at this point in the history
  • Loading branch information
dhayab committed Jan 24, 2024
1 parent 3f061b1 commit 7d82b01
Show file tree
Hide file tree
Showing 6 changed files with 44 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ type SearchBoxProps = {
refine?: (value: string) => void;
autofocus?: boolean;
searchAsYouType?: boolean;
ignoreCompositionEvents?: boolean;
isSearchStalled?: boolean;
disabled?: boolean;
ariaLabel?: string;
Expand All @@ -42,6 +43,7 @@ const defaultProps = {
showLoadingIndicator: true,
autofocus: false,
searchAsYouType: true,
ignoreCompositionEvents: false,
isSearchStalled: false,
disabled: false,
ariaLabel: 'Search',
Expand Down Expand Up @@ -88,8 +90,10 @@ class SearchBox extends Component<
const query = (event.target as HTMLInputElement).value;

if (
event.type === 'compositionend' ||
!(event as KeyboardEvent).isComposing
!(
this.props.ignoreCompositionEvents &&
(event as KeyboardEvent).isComposing
)
) {
if (searchAsYouType) {
refine(query);
Expand Down
11 changes: 11 additions & 0 deletions packages/instantsearch.js/src/widgets/search-box/search-box.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,12 @@ export type SearchBoxWidgetParams = {
* once `<Enter>` is pressed only.
*/
searchAsYouType?: boolean;
/**
* Whether to update the search state in the middle of a
* composition session.
* @default false
*/
ignoreCompositionEvents?: boolean;
/**
* Whether to show the reset button
*/
Expand Down Expand Up @@ -137,6 +143,7 @@ const renderer =
templates,
autofocus,
searchAsYouType,
ignoreCompositionEvents,
showReset,
showSubmit,
showLoadingIndicator,
Expand All @@ -147,6 +154,7 @@ const renderer =
templates: SearchBoxComponentTemplates;
autofocus: boolean;
searchAsYouType: boolean;
ignoreCompositionEvents: boolean;
showReset: boolean;
showSubmit: boolean;
showLoadingIndicator: boolean;
Expand All @@ -163,6 +171,7 @@ const renderer =
autofocus={autofocus}
refine={refine}
searchAsYouType={searchAsYouType}
ignoreCompositionEvents={ignoreCompositionEvents}
templates={templates}
showSubmit={showSubmit}
showReset={showReset}
Expand Down Expand Up @@ -195,6 +204,7 @@ const searchBox: SearchBoxWidget = function searchBox(widgetParams) {
cssClasses: userCssClasses = {},
autofocus = false,
searchAsYouType = true,
ignoreCompositionEvents = false,
showReset = true,
showSubmit = true,
showLoadingIndicator = true,
Expand Down Expand Up @@ -242,6 +252,7 @@ const searchBox: SearchBoxWidget = function searchBox(widgetParams) {
templates,
autofocus,
searchAsYouType,
ignoreCompositionEvents,
showReset,
showSubmit,
showLoadingIndicator,
Expand Down
20 changes: 13 additions & 7 deletions packages/react-instantsearch/src/widgets/SearchBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,19 @@ export type SearchBoxProps = Omit<
* @default true
*/
searchAsYouType?: boolean;
/**
* Whether to update the search state in the middle of a
* composition session.
* @default false
*/
ignoreCompositionEvents?: boolean;
translations?: Partial<UiProps['translations']>;
};

export function SearchBox({
queryHook,
searchAsYouType = true,
ignoreCompositionEvents = false,
translations,
...props
}: SearchBoxProps) {
Expand All @@ -44,10 +51,10 @@ export function SearchBox({
const [inputValue, setInputValue] = useState(query);
const inputRef = useRef<HTMLInputElement>(null);

function setQuery(newQuery: string, compositionComplete = true) {
function setQuery(newQuery: string, isComposing = false) {
setInputValue(newQuery);

if (searchAsYouType && compositionComplete) {
if (searchAsYouType && !(ignoreCompositionEvents && isComposing)) {
refine(newQuery);
}
}
Expand All @@ -63,11 +70,10 @@ export function SearchBox({
function onChange(
event: Parameters<NonNullable<SearchBoxUiComponentProps['onChange']>>[0]
) {
const compositionComplete =
event.type === 'compositionend' ||
!(event.nativeEvent as KeyboardEvent).isComposing;

setQuery(event.currentTarget.value, compositionComplete);
setQuery(
event.currentTarget.value,
(event.nativeEvent as KeyboardEvent).isComposing
);
}

function onSubmit(event: React.FormEvent<HTMLFormElement>) {
Expand Down
5 changes: 5 additions & 0 deletions packages/vue-instantsearch/src/components/SearchBox.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
:autofocus="autofocus"
:show-loading-indicator="showLoadingIndicator"
:should-show-loading-indicator="state.isSearchStalled"
:ignore-composition-events="ignoreCompositionEvents"
:submit-title="submitTitle"
:reset-title="resetTitle"
:class-names="classNames"
Expand Down Expand Up @@ -76,6 +77,10 @@ export default {
type: Boolean,
default: true,
},
ignoreCompositionEvents: {
type: Boolean,
default: false,
},
submitTitle: {
type: String,
default: 'Submit the search query',
Expand Down
6 changes: 5 additions & 1 deletion packages/vue-instantsearch/src/components/SearchInput.vue
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,10 @@ export default {
type: Boolean,
default: false,
},
ignoreCompositionEvents: {
type: Boolean,
default: false,
},
submitTitle: {
type: String,
default: 'Search',
Expand Down Expand Up @@ -161,7 +165,7 @@ export default {
return document.activeElement === this.$refs.input;
},
onInput(event) {
if (event.type === 'compositionend' || !event.isComposing) {
if (!(this.ignoreCompositionEvents && event.isComposing)) {
this.$emit('input', event.target.value);
this.$emit('update:modelValue', event.target.value);
}
Expand Down
6 changes: 4 additions & 2 deletions tests/common/widgets/search-box/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -308,15 +308,17 @@ export function createOptionsTests(
expect(screen.getByRole('searchbox')).toHaveValue('iPhone');
});

test('refines query only when composition is complete', async () => {
test('does not refine within composition session when `ignoreCompositionEvents: true`', async () => {
const searchClient = createSearchClient({});

await setup({
instantSearchOptions: {
indexName: 'indexName',
searchClient,
},
widgetParams: {},
widgetParams: {
ignoreCompositionEvents: true,
},
});

// Typing 木 using the Wubihua input method
Expand Down

0 comments on commit 7d82b01

Please sign in to comment.