diff --git a/docusaurus/docs/form/select/components/select.md b/docusaurus/docs/form/select/components/select.md
index fc93364340..3ac3329064 100644
--- a/docusaurus/docs/form/select/components/select.md
+++ b/docusaurus/docs/form/select/components/select.md
@@ -122,3 +122,13 @@ For example, if the new value is `{ "payer": { "name": "Availity", "id": "1" } }
payerNameAndId: opt => `${opt.payer.id} - ${opt.payer.name}`,
}
```
+
+### `selectByValue?: SelectByValue`
+
+Allows the value passed to be automatically selected in the dropdown. If the options are strings, pass the `value` property as the value to match on. If the dropdown options are objects, pass a `key` and `value` property to match the unique option where the `option[key]` value is equal to `value`.
+
+For example, to match an organization on the AvOrganizationSelect (the options are the entire organization object), you can match the `customerId` as the `key` to the `value` of `1234`
+
+```js
+selectByValue={{key: 'customerId', value: '1234'}}
+```
diff --git a/packages/select/src/ResourceSelect.js b/packages/select/src/ResourceSelect.js
index cd47313f56..a52442aea5 100644
--- a/packages/select/src/ResourceSelect.js
+++ b/packages/select/src/ResourceSelect.js
@@ -2,6 +2,7 @@ import React, { useRef, useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import qs from 'qs';
import get from 'lodash/get';
+import find from 'lodash/find';
import { useFormikContext } from 'formik';
import Select from './Select';
@@ -28,6 +29,7 @@ const ResourceSelect = ({
additionalPostGetArgs,
pageAll,
pageAllSearchBy,
+ selectByValue,
...rest
}) => {
const { setFieldValue } = useFormikContext();
@@ -37,6 +39,13 @@ const ResourceSelect = ({
const [previousOptions, setPreviousOptions] = useState([]);
const [numTimesResourceCalled, setNumTimesResourceCalled] = useState(0);
+ const getValueKey = (attrs = rest) => get(attrs, 'valueKey', 'value');
+
+ const getOptionValue = (option) =>
+ rest.raw && !rest.valueKey
+ ? option
+ : get(option, getValueKey(rest), option);
+
if (_cacheUniq === undefined && watchParams) {
const params = {
customerId: rest.customerId,
@@ -58,6 +67,19 @@ const ResourceSelect = ({
setNumTimesResourceCalled(0);
}, [_cacheUniq]);
+ useEffect(() => {
+ if (selectByValue && previousOptions?.length >= 1) {
+ const matchedOption = find(
+ previousOptions,
+ (option) =>
+ getOptionValue(option)?.[selectByValue?.key] ===
+ selectByValue?.value ||
+ getOptionValue(option) === selectByValue?.value
+ );
+ setFieldValue(name, matchedOption);
+ }
+ }, [selectByValue, setFieldValue, previousOptions]);
+
const onFocusHandler = (...args) => {
if (onFocus) onFocus(...args);
};
@@ -356,6 +378,10 @@ ResourceSelect.propTypes = {
pageAll: PropTypes.bool,
pageAllSearchBy: PropTypes.func,
onError: PropTypes.func,
+ selectByValue: PropTypes.shape({
+ value: PropTypes.string,
+ key: PropTypes.string,
+ }),
};
ResourceSelect.defaultProps = {
diff --git a/packages/select/src/Select.js b/packages/select/src/Select.js
index d421b5f03f..70e645cf6b 100644
--- a/packages/select/src/Select.js
+++ b/packages/select/src/Select.js
@@ -1,4 +1,4 @@
-import React, { useState } from 'react';
+import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { useField, useFormikContext } from 'formik';
@@ -7,6 +7,7 @@ import Creatable from 'react-select/creatable';
import { AsyncPaginate as Async } from 'react-select-async-paginate';
import get from 'lodash/get';
import has from 'lodash/has';
+import find from 'lodash/find';
import isFunction from 'lodash/isFunction';
import isEqual from 'lodash/isEqual';
@@ -79,6 +80,49 @@ const Select = ({
const [newOptions, setNewOptions] = useState([]);
+ const [selectOptions, setSelectOptions] = useState([]);
+
+ useEffect(() => {
+ if (!attributes.loadOptions) {
+ if (allowSelectAll && attributes.isMulti) {
+ if (
+ [...options, ...newOptions].length > 0 &&
+ (values[name] === undefined ||
+ values[name] === null ||
+ values[name].length < [...options, ...newOptions].length)
+ ) {
+ validateSelectAllOptions([...options, ...newOptions]);
+ setSelectOptions([selectAllOption, ...options, ...newOptions]);
+ } else {
+ setSelectOptions([...options, ...newOptions]);
+ }
+ } else {
+ setSelectOptions([...options, ...newOptions]);
+ }
+ }
+ }, [
+ attributes.loadOptions,
+ allowSelectAll,
+ attributes.isMulti,
+ newOptions,
+ options,
+ setSelectOptions,
+ ]);
+
+ useEffect(() => {
+ // auto select an option
+ if (attributes.selectByValue && selectOptions?.length >= 1) {
+ const matchedOption = find(
+ options,
+ (option) =>
+ getOptionValue(option)?.[attributes.selectByValue?.key] ===
+ attributes.selectByValue?.value ||
+ getOptionValue(option) === attributes.selectByValue?.value
+ );
+ setFieldValue(name, matchedOption);
+ }
+ }, [attributes.selectByValue]);
+
let _cacheUniq = attributes.cacheUniq;
if (!Array.isArray(_cacheUniq)) {
@@ -248,25 +292,6 @@ const Select = ({
}
};
- let selectOptions;
- if (!attributes.loadOptions) {
- if (allowSelectAll && attributes.isMulti) {
- if (
- [...options, ...newOptions].length > 0 &&
- (values[name] === undefined ||
- values[name] === null ||
- values[name].length < [...options, ...newOptions].length)
- ) {
- validateSelectAllOptions([...options, ...newOptions]);
- selectOptions = [selectAllOption, ...options, ...newOptions];
- } else {
- selectOptions = [...options, ...newOptions];
- }
- } else {
- selectOptions = [...options, ...newOptions];
- }
- }
-
if (attributes.loadOptions && allowSelectAll) {
// eslint-disable-next-line no-console
console.warn('allowSelectAll is ignored when loadOptions is defined.');
@@ -389,6 +414,10 @@ Select.propTypes = {
autofill: PropTypes.oneOfType([PropTypes.bool, PropTypes.object]),
allowSelectAll: PropTypes.bool,
waitUntilFocused: PropTypes.bool,
+ selectByValue: PropTypes.shape({
+ value: PropTypes.string,
+ key: PropTypes.string,
+ }),
};
export default Select;
diff --git a/packages/select/tests/ResourceSelect.test.js b/packages/select/tests/ResourceSelect.test.js
index ccf1745021..bb0c924407 100644
--- a/packages/select/tests/ResourceSelect.test.js
+++ b/packages/select/tests/ResourceSelect.test.js
@@ -569,6 +569,136 @@ describe('ResourceSelect', () => {
});
});
+describe('SelectByValue', () => {
+ afterEach(() => {
+ jest.clearAllMocks();
+ cleanup();
+ });
+ it('selects the matching selectByValue option by string', async () => {
+ avRegionsApi.postGet.mockResolvedValue({
+ data: {
+ regions: [
+ {
+ id: 'FL',
+ value: 'Florida',
+ },
+ {
+ id: 'TX',
+ value: 'Texas',
+ },
+ ],
+ },
+ });
+
+ const { getByText } = renderSelect({
+ resource: avRegionsApi,
+ labelKey: 'value',
+ valueKey: 'id',
+ classNamePrefix: 'test__regions',
+ getResult: 'regions',
+ selectByValue: { value: 'TX' },
+ });
+
+ await waitFor(() => {
+ expect(avRegionsApi.postGet).toHaveBeenCalledTimes(1);
+ });
+ await fireEvent.click(getByText('Submit'));
+
+ await waitFor(() => {
+ expect(onSubmit).toHaveBeenCalledWith(
+ expect.objectContaining({
+ 'test-form-input': {
+ id: 'TX',
+ value: 'Texas',
+ },
+ }),
+ expect.anything()
+ );
+ });
+ });
+
+ it('selects the matching selectByValue option by key', async () => {
+ avRegionsApi.postGet.mockResolvedValue({
+ data: {
+ regions: [
+ {
+ label: 'Florida',
+ value: {
+ foo: 'FL',
+ bar: '123',
+ },
+ },
+ {
+ label: 'Texas',
+ value: {
+ foo: 'TX',
+ bar: '456',
+ },
+ },
+ ],
+ },
+ });
+
+ const { getByText } = renderSelect({
+ resource: avRegionsApi,
+ classNamePrefix: 'test__regions',
+ getResult: 'regions',
+ selectByValue: { key: 'foo', value: 'TX' },
+ });
+
+ await waitFor(() => {
+ expect(avRegionsApi.postGet).toHaveBeenCalledTimes(1);
+ });
+ await fireEvent.click(getByText('Submit'));
+
+ await waitFor(() => {
+ expect(onSubmit).toHaveBeenCalledWith(
+ expect.objectContaining({
+ 'test-form-input': {
+ label: 'Texas',
+ value: {
+ foo: 'TX',
+ bar: '456',
+ },
+ },
+ }),
+ expect.anything()
+ );
+ });
+ });
+
+ it('does not select an option when none match', async () => {
+ avRegionsApi.postGet.mockResolvedValue({
+ data: {
+ regions: [
+ {
+ id: 'FL',
+ value: 'Florida',
+ },
+ {
+ id: 'TX',
+ value: 'Texas',
+ },
+ ],
+ },
+ });
+
+ const { getByText } = renderSelect({
+ resource: avRegionsApi,
+ labelKey: 'value',
+ valueKey: 'id',
+ classNamePrefix: 'test__regions',
+ getResult: 'regions',
+ selectByValue: { value: 'KY' },
+ });
+
+ await fireEvent.click(getByText('Submit'));
+ await waitFor(() => {
+ expect(onSubmit).not.toHaveBeenCalled();
+ });
+ });
+});
+
// -----
const renderResourceSelect = (props) => {
const Component = () => {
@@ -608,168 +738,168 @@ const renderResourceSelect = (props) => {
return render(