diff --git a/playbook/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx b/playbook/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx
index cb5d53c031..7e3f13b1a0 100644
--- a/playbook/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx
+++ b/playbook/app/pb_kits/playbook/pb_dropdown/_dropdown.tsx
@@ -112,6 +112,8 @@ type DropdownProps = {
isClosed?: boolean;
label?: string;
multiSelect?: boolean;
+ name?: string;
+ onChange?: (event: { target: { name?: string; value: any } }) => void;
onSelect?: (arg: GenericObject) => null;
options?: GenericObject;
placeholder?: string;
@@ -156,6 +158,8 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
label,
multiSelect = false,
formPillProps,
+ name,
+ onChange,
onSelect,
options,
placeholder,
@@ -414,6 +418,10 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
setIsDropDownClosed(false);
};
+ const handleSelectionChange = (value: any) => {
+ onSelect && onSelect(value);
+ onChange && onChange({ target: { name, value } });
+ };
const handleOptionClick = (clickedItem: GenericObject) => {
if (disabled) return;
@@ -426,7 +434,7 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
const next = exists
? list.filter((option) => option.value !== clickedItem.value)
: [...list, clickedItem];
- onSelect && onSelect(next);
+ handleSelectionChange(next);
return next;
});
setFilterItem("");
@@ -439,7 +447,7 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
if (shouldCloseOnClick) {
setIsDropDownClosed(true);
}
- onSelect && onSelect(clickedItem);
+ handleSelectionChange(clickedItem);
// Sync with DatePickers if this is a quickpick variant
if (variant === "quickpick" && Array.isArray(clickedItem.value)) {
@@ -468,10 +476,10 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
if (disabled) return;
if (multiSelect) {
setSelected([]);
- onSelect && onSelect([]);
+ handleSelectionChange([]);
} else {
setSelected({});
- onSelect && onSelect(null);
+ handleSelectionChange(null);
setFocusedOptionIndex(-1);
setFilterItem("");
@@ -505,10 +513,10 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
clearSelected: () => {
if (multiSelect) {
setSelected([]);
- onSelect && onSelect([]);
+ handleSelectionChange([]);
} else {
setSelected({});
- onSelect && onSelect(null);
+ handleSelectionChange(null);
}
setFilterItem("");
setIsDropDownClosed(true);
@@ -522,16 +530,16 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
clearSelected: () => {
if (multiSelect) {
setSelected([]);
- onSelect && onSelect([]);
+ handleSelectionChange([]);
} else {
setSelected({});
- onSelect && onSelect(null);
+ handleSelectionChange(null);
}
setFilterItem("");
setIsDropDownClosed(true);
},
};
- }, [multiSelect, onSelect, setSelected, setFilterItem, setIsDropDownClosed]);
+ }, [multiSelect, handleSelectionChange, setSelected, setFilterItem, setIsDropDownClosed]);
useImperativeHandle(ref, () => imperativeRef.current);
@@ -594,6 +602,7 @@ let Dropdown = (props: DropdownProps, ref: any): React.ReactElement | null => {
handleBackspace,
handleChange,
handleOptionClick,
+ handleSelectionChange,
handleWrapperClick,
inputRef,
inputWrapperRef,
diff --git a/playbook/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_multi_select_react_hook.jsx b/playbook/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_multi_select_react_hook.jsx
new file mode 100644
index 0000000000..55a2485f99
--- /dev/null
+++ b/playbook/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_multi_select_react_hook.jsx
@@ -0,0 +1,45 @@
+import React from 'react'
+
+import Dropdown from '../_dropdown'
+import Title from '../../pb_title/_title'
+import { useForm } from 'react-hook-form'
+
+const options = [
+ { label: 'United States', value: 'unitedStates', id: 'us' },
+ { label: 'United Kingdom', value: 'unitedKingdom', id: 'gb' },
+ { label: 'Canada', value: 'canada', id: 'ca' },
+ { label: 'Pakistan', value: 'pakistan', id: 'pk' },
+ { label: 'India', value: 'india', id: 'in' },
+ { label: 'Australia', value: 'australia', id: 'au' },
+ { label: 'New Zealand', value: 'new Zealand', id: 'nz' },
+ { label: 'Italy', value: 'italy', id: 'it' },
+ { label: 'Spain', value: 'spain', id: 'es' },
+]
+
+const DropdownMultiSelectReactHook = (props) => {
+ const { register, watch } = useForm()
+
+ const selectedCountries = watch('countries')
+
+ return (
+ <>
+
{`${country.label} - ${country.value}`}
+ ))} + > + ) +} + +export default DropdownMultiSelectReactHook diff --git a/playbook/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_multi_select_react_hook.md b/playbook/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_multi_select_react_hook.md new file mode 100644 index 0000000000..41f34c1df5 --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_multi_select_react_hook.md @@ -0,0 +1 @@ +You can pass `react-hook-form` props to a multi-select Dropdown. Spread `register` onto Dropdown with `multiSelect` to keep the selected options array in form state. diff --git a/playbook/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_react_hook.jsx b/playbook/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_react_hook.jsx new file mode 100644 index 0000000000..b3bd5f8e6a --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_react_hook.jsx @@ -0,0 +1,36 @@ +import React from 'react' + +import Dropdown from '../_dropdown' +import Title from '../../pb_title/_title' +import { useForm } from 'react-hook-form' + +const options = [ + { label: 'United States', value: 'unitedStates', id: 'us' }, + { label: 'Canada', value: 'canada', id: 'ca' }, + { label: 'Pakistan', value: 'pakistan', id: 'pk' }, +] + +const DropdownReactHook = (props) => { + const { register, watch } = useForm() + + const selectedCountry = watch('country') + + return ( + <> +{selectedCountry && `${selectedCountry.label} - ${selectedCountry.value}`}
+ > + ) +} + +export default DropdownReactHook diff --git a/playbook/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_react_hook.md b/playbook/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_react_hook.md new file mode 100644 index 0000000000..4d34d51c78 --- /dev/null +++ b/playbook/app/pb_kits/playbook/pb_dropdown/docs/_dropdown_react_hook.md @@ -0,0 +1 @@ +You can pass `react-hook-form` props to the Dropdown kit. Spread `register` onto a single-select Dropdown to keep the selected option in form state. diff --git a/playbook/app/pb_kits/playbook/pb_dropdown/docs/_playground.json b/playbook/app/pb_kits/playbook/pb_dropdown/docs/_playground.json index fdd2745a46..42a9569feb 100644 --- a/playbook/app/pb_kits/playbook/pb_dropdown/docs/_playground.json +++ b/playbook/app/pb_kits/playbook/pb_dropdown/docs/_playground.json @@ -88,6 +88,7 @@ "name": "Content", "props": [ "label", + "name", "placeholder", "options", "defaultValue", @@ -130,6 +131,7 @@ { "name": "Events", "props": [ + "onChange", "onSelect" ] } diff --git a/playbook/app/pb_kits/playbook/pb_dropdown/docs/_playground.overrides.json b/playbook/app/pb_kits/playbook/pb_dropdown/docs/_playground.overrides.json index cc84af3363..b02dcb5c4d 100644 --- a/playbook/app/pb_kits/playbook/pb_dropdown/docs/_playground.overrides.json +++ b/playbook/app/pb_kits/playbook/pb_dropdown/docs/_playground.overrides.json @@ -70,6 +70,7 @@ "name": "Content", "props": [ "label", + "name", "placeholder", "options", "defaultValue", @@ -111,7 +112,7 @@ }, { "name": "Events", - "props": ["onSelect"] + "props": ["onChange", "onSelect"] } ], "presets": [ diff --git a/playbook/app/pb_kits/playbook/pb_dropdown/docs/example.yml b/playbook/app/pb_kits/playbook/pb_dropdown/docs/example.yml index 42b288f5d4..f575657d02 100644 --- a/playbook/app/pb_kits/playbook/pb_dropdown/docs/example.yml +++ b/playbook/app/pb_kits/playbook/pb_dropdown/docs/example.yml @@ -81,3 +81,6 @@ examples: - dropdown_required_indicator: Required Indicator - dropdown_disabled: Disabled Input - dropdown_grouped_options: Grouped Options + - dropdown_react_hook: React Hook + - dropdown_multi_select_react_hook: React Hook Multi Select + diff --git a/playbook/app/pb_kits/playbook/pb_dropdown/docs/index.js b/playbook/app/pb_kits/playbook/pb_dropdown/docs/index.js index c1e4b9e75a..57eea9f555 100644 --- a/playbook/app/pb_kits/playbook/pb_dropdown/docs/index.js +++ b/playbook/app/pb_kits/playbook/pb_dropdown/docs/index.js @@ -1,4 +1,6 @@ export { default as DropdownDefault } from './_dropdown_default.jsx' +export { default as DropdownReactHook } from './_dropdown_react_hook.jsx' +export { default as DropdownMultiSelectReactHook } from './_dropdown_multi_select_react_hook.jsx' export { default as DropdownWithCustomDisplay } from './_dropdown_with_custom_display.jsx' export { default as DropdownWithCustomOptions } from './_dropdown_with_custom_options.jsx' export { default as DropdownWithCustomTrigger } from './_dropdown_with_custom_trigger.jsx' diff --git a/playbook/app/pb_kits/playbook/pb_dropdown/dropdown.test.jsx b/playbook/app/pb_kits/playbook/pb_dropdown/dropdown.test.jsx index 214e601c3f..e76ac511d7 100644 --- a/playbook/app/pb_kits/playbook/pb_dropdown/dropdown.test.jsx +++ b/playbook/app/pb_kits/playbook/pb_dropdown/dropdown.test.jsx @@ -1,5 +1,5 @@ import React, { useState } from "react" -import { render, screen, fireEvent, waitFor } from "../utilities/test-utils" +import { render, screen, fireEvent, waitFor, act } from "../utilities/test-utils" import { Dropdown, Icon, IconCircle } from 'playbook-ui' import DateTime from "../pb_kit/dateTime.ts" @@ -995,4 +995,207 @@ test('disabled prop disables autocomplete input', () => { const input = kit.querySelector('.dropdown_input') expect(input).toBeDisabled() -}) \ No newline at end of file +}) + +test('onChange uses react-hook-form event shape', () => { + const onChange = jest.fn() + + render( +