Skip to content

Commit

Permalink
fix(refinement-list): re-apply focus on facet checkbox after render (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
dhayab authored Oct 21, 2024
1 parent 51f94c2 commit 17e6658
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,11 @@ class RefinementList<TTemplates extends Templates> extends Component<
> {
public static defaultProps = defaultProps;

private listRef = createRef<HTMLUListElement>();
private searchBox = createRef<SearchBox>();

private lastRefinedValue: string | undefined = undefined;

public shouldComponentUpdate(
nextProps: RefinementListPropsWithDefaultProps<TTemplates>
) {
Expand All @@ -122,6 +125,7 @@ class RefinementList<TTemplates extends Templates> extends Component<
}

private refine(facetValueToRefine: string) {
this.lastRefinedValue = facetValueToRefine;
this.props.toggleRefinement(facetValueToRefine);
}

Expand Down Expand Up @@ -270,6 +274,20 @@ class RefinementList<TTemplates extends Templates> extends Component<
}
}

/**
* This sets focus on the last refined input element after a render
* because Preact does not perform it automatically.
* @see https://github.com/preactjs/preact/issues/3242
*/
public componentDidUpdate() {
this.listRef.current
?.querySelector<HTMLInputElement>(
`input[value="${this.lastRefinedValue}"]`
)
?.focus();
this.lastRefinedValue = undefined;
}

private refineFirstValue() {
const firstValue = this.props.facetValues && this.props.facetValues[0];
if (firstValue) {
Expand Down Expand Up @@ -330,7 +348,7 @@ class RefinementList<TTemplates extends Templates> extends Component<

const facetValues = this.props.facetValues &&
this.props.facetValues.length > 0 && (
<ul className={this.props.cssClasses.list}>
<ul ref={this.listRef} className={this.props.cssClasses.list}>
{this.props.facetValues.map(this._generateFacetItem, this)}
</ul>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -660,7 +660,7 @@ describe('RefinementList', () => {
<label>
<input type="radio" checked="${item.isRefined}" />
${item.value}
</span>
</label>
`,
showMoreText: '',
};
Expand Down
41 changes: 41 additions & 0 deletions tests/common/widgets/refinement-list/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,47 @@ export function createOptionsTests(
]);
});

test('keeps focus on toggled input between re-renders', async () => {
const searchClient = createMockedSearchClient();

await setup({
instantSearchOptions: {
indexName: 'indexName',
searchClient,
},
widgetParams: { attribute: 'brand' },
});

await act(async () => {
await wait(0);
});

expect(document.activeElement).toEqual(document.body);

const initialTargetItem = document.querySelector(
'.ais-RefinementList-checkbox[value="Samsung"]'
)!;

await act(async () => {
/**
* Jest wrongly fails the last assertion when running in Vue 3
* (`activeElement` is reset to body).
* Duplicating the following call fixes this as a workaround,
* and doesn't change the objective of this test.
*/
userEvent.click(initialTargetItem);
userEvent.click(initialTargetItem);
expect(document.activeElement).toEqual(initialTargetItem);
await wait(0);
});

const updatedTargetItem = document.querySelector(
'.ais-RefinementList-checkbox[value="Samsung"]'
)!;

expect(document.activeElement).toEqual(updatedTargetItem);
});

describe('sorting', () => {
test('sorts the items by ascending name', async () => {
const searchClient = createMockedSearchClient();
Expand Down

0 comments on commit 17e6658

Please sign in to comment.