Skip to content

Commit

Permalink
feat: Render non-searchable items in AutoComplete
Browse files Browse the repository at this point in the history
Since it can sometimes be helpful to render additional items that are
not searchable and mostly for presentation, the AutoComplete was updated
to have `beforeResultsChildren` and `afterResultsChildren` props. The
`afterResultsChildren` prop is good to use for fetching additional
results for paginated auto completion.
  • Loading branch information
mlaursen committed Jul 12, 2020
1 parent 56cda1f commit e7a82ac
Show file tree
Hide file tree
Showing 4 changed files with 275 additions and 0 deletions.
6 changes: 6 additions & 0 deletions packages/autocomplete/src/AutoComplete.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ const AutoComplete = forwardRef<HTMLInputElement, AutoCompleteProps>(
omitKeys = EMPTY_LIST,
value: propValue,
defaultValue,
beforeResultsChildren,
afterResultsChildren,
...props
},
forwardedRef
Expand Down Expand Up @@ -185,6 +187,7 @@ const AutoComplete = forwardRef<HTMLInputElement, AutoCompleteProps>(
style={fixedStyle}
className={cn(listbox({ temporary: true }), listboxClassName)}
>
{beforeResultsChildren}
{filteredData.map((datum, i) => {
const resultId = getResultId(suggestionsId, i);
let optionProps: ListboxOptionProps | undefined;
Expand Down Expand Up @@ -215,6 +218,7 @@ const AutoComplete = forwardRef<HTMLInputElement, AutoCompleteProps>(
</Option>
);
})}
{afterResultsChildren}
</List>
</ScaleTransition>
</>
Expand Down Expand Up @@ -307,6 +311,8 @@ if (process.env.NODE_ENV !== "production") {
omitKeys: PropTypes.arrayOf(PropTypes.string),
value: PropTypes.string,
defaultValue: PropTypes.string,
beforeResultsChildren: PropTypes.node,
afterResultsChildren: PropTypes.node,
};
} catch (e) {}
}
Expand Down
42 changes: 42 additions & 0 deletions packages/autocomplete/src/__tests__/AutoComplete.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -195,4 +195,46 @@ describe("AutoComplete", () => {
expect(getListbox).toThrow();
fireEvent.blur(input);
});

it("should be able to render content before and after the matching data", () => {
const beforeResultsChildren = <div>Before Results</div>;
const afterResultsChildren = <div>After Results</div>;
const { getByText, getByRole } = render(
<AutoComplete
{...PROPS}
data={states.slice(0, 5)}
beforeResultsChildren={beforeResultsChildren}
afterResultsChildren={afterResultsChildren}
/>
);

const input = getById<HTMLInputElement>("autocomplete");
const getListbox = () => getByRole("listbox");
const getBeforeResults = () => getByText("Before Results");
const getAfterResults = () => getByText("After Results");

expect(getBeforeResults).toThrow();
expect(getAfterResults).toThrow();

fireEvent.focus(input);
expect(getBeforeResults).not.toThrow();
expect(getAfterResults).not.toThrow();

const listbox = getListbox();
expect(listbox.firstChild?.textContent).toBe("Before Results");
expect(listbox.lastChild?.textContent).toBe("After Results");
expect(listbox).toMatchSnapshot();

// filtered matches
fireEvent.change(input, { value: "Am" });
expect(listbox.firstChild?.textContent).toBe("Before Results");
expect(listbox.lastChild?.textContent).toBe("After Results");
expect(listbox).toMatchSnapshot();

// no matches
fireEvent.change(input, { value: "Aml" });
expect(listbox.firstChild?.textContent).toBe("Before Results");
expect(listbox.lastChild?.textContent).toBe("After Results");
expect(listbox).toMatchSnapshot();
});
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,218 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`AutoComplete should be able to render content before and after the matching data 1`] = `
<ul
class="rmd-list rmd-listbox rmd-listbox--temporary rmd-transition--scale-y-enter rmd-transition--scale-y-enter-active"
id="autocomplete-listbox"
role="listbox"
style="left: 0px; top: 0px; width: 0px; position: fixed; transform-origin: 50% 0;"
>
<div>
Before Results
</div>
<li
class="rmd-list-item rmd-list-item--clickable rmd-option"
id="autocomplete-listbox-result-1"
role="option"
>
<span
class="rmd-list-item__text"
>
Alabama
</span>
</li>
<li
class="rmd-list-item rmd-list-item--clickable rmd-option"
id="autocomplete-listbox-result-2"
role="option"
>
<span
class="rmd-list-item__text"
>
Alaska
</span>
</li>
<li
class="rmd-list-item rmd-list-item--clickable rmd-option"
id="autocomplete-listbox-result-3"
role="option"
>
<span
class="rmd-list-item__text"
>
American Samoa
</span>
</li>
<li
class="rmd-list-item rmd-list-item--clickable rmd-option"
id="autocomplete-listbox-result-4"
role="option"
>
<span
class="rmd-list-item__text"
>
Arizona
</span>
</li>
<li
class="rmd-list-item rmd-list-item--clickable rmd-option"
id="autocomplete-listbox-result-5"
role="option"
>
<span
class="rmd-list-item__text"
>
Arkansas
</span>
</li>
<div>
After Results
</div>
</ul>
`;

exports[`AutoComplete should be able to render content before and after the matching data 2`] = `
<ul
class="rmd-list rmd-listbox rmd-listbox--temporary rmd-transition--scale-y-enter rmd-transition--scale-y-enter-active"
id="autocomplete-listbox"
role="listbox"
style="left: 0px; top: 0px; width: 0px; position: fixed; transform-origin: 50% 0;"
>
<div>
Before Results
</div>
<li
class="rmd-list-item rmd-list-item--clickable rmd-option"
id="autocomplete-listbox-result-1"
role="option"
>
<span
class="rmd-list-item__text"
>
Alabama
</span>
</li>
<li
class="rmd-list-item rmd-list-item--clickable rmd-option"
id="autocomplete-listbox-result-2"
role="option"
>
<span
class="rmd-list-item__text"
>
Alaska
</span>
</li>
<li
class="rmd-list-item rmd-list-item--clickable rmd-option"
id="autocomplete-listbox-result-3"
role="option"
>
<span
class="rmd-list-item__text"
>
American Samoa
</span>
</li>
<li
class="rmd-list-item rmd-list-item--clickable rmd-option"
id="autocomplete-listbox-result-4"
role="option"
>
<span
class="rmd-list-item__text"
>
Arizona
</span>
</li>
<li
class="rmd-list-item rmd-list-item--clickable rmd-option"
id="autocomplete-listbox-result-5"
role="option"
>
<span
class="rmd-list-item__text"
>
Arkansas
</span>
</li>
<div>
After Results
</div>
</ul>
`;

exports[`AutoComplete should be able to render content before and after the matching data 3`] = `
<ul
class="rmd-list rmd-listbox rmd-listbox--temporary rmd-transition--scale-y-enter rmd-transition--scale-y-enter-active"
id="autocomplete-listbox"
role="listbox"
style="left: 0px; top: 0px; width: 0px; position: fixed; transform-origin: 50% 0;"
>
<div>
Before Results
</div>
<li
class="rmd-list-item rmd-list-item--clickable rmd-option"
id="autocomplete-listbox-result-1"
role="option"
>
<span
class="rmd-list-item__text"
>
Alabama
</span>
</li>
<li
class="rmd-list-item rmd-list-item--clickable rmd-option"
id="autocomplete-listbox-result-2"
role="option"
>
<span
class="rmd-list-item__text"
>
Alaska
</span>
</li>
<li
class="rmd-list-item rmd-list-item--clickable rmd-option"
id="autocomplete-listbox-result-3"
role="option"
>
<span
class="rmd-list-item__text"
>
American Samoa
</span>
</li>
<li
class="rmd-list-item rmd-list-item--clickable rmd-option"
id="autocomplete-listbox-result-4"
role="option"
>
<span
class="rmd-list-item__text"
>
Arizona
</span>
</li>
<li
class="rmd-list-item rmd-list-item--clickable rmd-option"
id="autocomplete-listbox-result-5"
role="option"
>
<span
class="rmd-list-item__text"
>
Arkansas
</span>
</li>
<div>
After Results
</div>
</ul>
`;

exports[`AutoComplete should handle a normal filter flow 1`] = `
<div>
<div
Expand Down
14 changes: 14 additions & 0 deletions packages/autocomplete/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -289,4 +289,18 @@ export interface AutoCompleteProps
*```
*/
omitKeys?: readonly string[];

/**
* Any optional children to display before the matched results in the
* autocomplete's menu. This should normally be for any presentational data or
* things that should not be searchable.
*/
beforeResultsChildren?: ReactNode;

/**
* Any optional children to display after the matched results in the
* autocomplete's menu. This should normally be for any presentational data or
* things that should not be searchable.
*/
afterResultsChildren?: ReactNode;
}

0 comments on commit e7a82ac

Please sign in to comment.