From 7cf934b89b28309d447e17143b45b1a1d0ecdab1 Mon Sep 17 00:00:00 2001 From: Divyanshu Gupta Date: Thu, 10 Apr 2025 23:29:35 +0530 Subject: [PATCH 01/10] chore(dropdown): add split button action example --- .../components/Dropdown/examples/Dropdown.md | 8 ++ .../Dropdown/examples/DropdownWithSplit.tsx | 79 +++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 packages/react-core/src/components/Dropdown/examples/DropdownWithSplit.tsx diff --git a/packages/react-core/src/components/Dropdown/examples/Dropdown.md b/packages/react-core/src/components/Dropdown/examples/Dropdown.md index 831bd97a5cf..edb5d77995a 100644 --- a/packages/react-core/src/components/Dropdown/examples/Dropdown.md +++ b/packages/react-core/src/components/Dropdown/examples/Dropdown.md @@ -63,3 +63,11 @@ To provide users with more context about a ``, pass a short messag ```ts file="./DropdownWithDescriptions.tsx" ``` + +### Split button + +Description of split button + +```ts file="./DropdownWithSplit.tsx" + +``` diff --git a/packages/react-core/src/components/Dropdown/examples/DropdownWithSplit.tsx b/packages/react-core/src/components/Dropdown/examples/DropdownWithSplit.tsx new file mode 100644 index 00000000000..16b04f53f74 --- /dev/null +++ b/packages/react-core/src/components/Dropdown/examples/DropdownWithSplit.tsx @@ -0,0 +1,79 @@ +import { + Dropdown, + MenuToggle, + MenuToggleCheckbox, + DropdownItem, + DropdownList, + Divider, + MenuToggleElement +} from '@patternfly/react-core'; + +export const DropdownSplitButtonText: React.FunctionComponent = () => { + const [isOpen, setIsOpen] = React.useState(false); + + const onToggleClick = () => { + setIsOpen(!isOpen); + }; + + const onFocus = () => { + const element = document.getElementById('toggle-split-button-text'); + element?.focus(); + }; + + const onSelect = () => { + setIsOpen(false); + onFocus(); + }; + + return ( + ) => ( + ]} + ref={toggleRef} + onClick={onToggleClick} + isExpanded={isOpen} + /> + )} + isOpen={isOpen} + > + + + Action + + ev.preventDefault()} + > + Link + + + Disabled Action + + + Disabled Link + + + Aria-disabled Link + + + + Separated Action + + ev.preventDefault()}> + Separated Link + + + + ); +}; From 5d24223eee4d4ff3952a6e9eeec219990f57f70f Mon Sep 17 00:00:00 2001 From: Divyanshu Gupta Date: Thu, 10 Apr 2025 23:47:39 +0530 Subject: [PATCH 02/10] added aria label --- .../components/Dropdown/examples/DropdownWithSplit.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/react-core/src/components/Dropdown/examples/DropdownWithSplit.tsx b/packages/react-core/src/components/Dropdown/examples/DropdownWithSplit.tsx index 16b04f53f74..9259ccbd9dd 100644 --- a/packages/react-core/src/components/Dropdown/examples/DropdownWithSplit.tsx +++ b/packages/react-core/src/components/Dropdown/examples/DropdownWithSplit.tsx @@ -30,7 +30,13 @@ export const DropdownSplitButtonText: React.FunctionComponent = () => { onSelect={onSelect} toggle={(toggleRef: React.Ref) => ( ]} + splitButtonItems={[ + + ]} ref={toggleRef} onClick={onToggleClick} isExpanded={isOpen} From 252976395d61644a58fa086cacad4d6bd1235a60 Mon Sep 17 00:00:00 2001 From: Divyanshu Gupta Date: Thu, 10 Apr 2025 23:51:50 +0530 Subject: [PATCH 03/10] added aria label --- .../components/Dropdown/examples/DropdownWithSplit.tsx | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/react-core/src/components/Dropdown/examples/DropdownWithSplit.tsx b/packages/react-core/src/components/Dropdown/examples/DropdownWithSplit.tsx index 9259ccbd9dd..ab747acdf1b 100644 --- a/packages/react-core/src/components/Dropdown/examples/DropdownWithSplit.tsx +++ b/packages/react-core/src/components/Dropdown/examples/DropdownWithSplit.tsx @@ -30,13 +30,8 @@ export const DropdownSplitButtonText: React.FunctionComponent = () => { onSelect={onSelect} toggle={(toggleRef: React.Ref) => ( - ]} + splitButtonItems={[]} + aria-label="Split button dropdown" ref={toggleRef} onClick={onToggleClick} isExpanded={isOpen} From 3aea271efcc7ac40b6bb89d0b2d569f2a1d04d29 Mon Sep 17 00:00:00 2001 From: Divyanshu Gupta Date: Fri, 11 Apr 2025 00:13:03 +0530 Subject: [PATCH 04/10] added aria labels --- .../components/Dropdown/examples/DropdownWithSplit.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/react-core/src/components/Dropdown/examples/DropdownWithSplit.tsx b/packages/react-core/src/components/Dropdown/examples/DropdownWithSplit.tsx index ab747acdf1b..4e3673867d8 100644 --- a/packages/react-core/src/components/Dropdown/examples/DropdownWithSplit.tsx +++ b/packages/react-core/src/components/Dropdown/examples/DropdownWithSplit.tsx @@ -30,8 +30,14 @@ export const DropdownSplitButtonText: React.FunctionComponent = () => { onSelect={onSelect} toggle={(toggleRef: React.Ref) => ( ]} - aria-label="Split button dropdown" + splitButtonItems={[ + + ]} + aria-label="Dropdown with checkbox split button" ref={toggleRef} onClick={onToggleClick} isExpanded={isOpen} From f85a1789af4d1be23d5a746caa6c62a14c04285f Mon Sep 17 00:00:00 2001 From: Divyanshu Gupta Date: Thu, 24 Apr 2025 01:25:41 +0530 Subject: [PATCH 05/10] added the onOpenChange prop --- .../src/components/Dropdown/examples/DropdownWithSplit.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/react-core/src/components/Dropdown/examples/DropdownWithSplit.tsx b/packages/react-core/src/components/Dropdown/examples/DropdownWithSplit.tsx index 4e3673867d8..dc707492b08 100644 --- a/packages/react-core/src/components/Dropdown/examples/DropdownWithSplit.tsx +++ b/packages/react-core/src/components/Dropdown/examples/DropdownWithSplit.tsx @@ -28,6 +28,7 @@ export const DropdownSplitButtonText: React.FunctionComponent = () => { return ( setIsOpen(isOpen)} toggle={(toggleRef: React.Ref) => ( Date: Thu, 29 May 2025 20:56:32 +0530 Subject: [PATCH 06/10] added focus logic --- .../Dropdown/examples/DropdownWithSplit.tsx | 38 ++++++++++++------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/packages/react-core/src/components/Dropdown/examples/DropdownWithSplit.tsx b/packages/react-core/src/components/Dropdown/examples/DropdownWithSplit.tsx index dc707492b08..3f06e1c3fce 100644 --- a/packages/react-core/src/components/Dropdown/examples/DropdownWithSplit.tsx +++ b/packages/react-core/src/components/Dropdown/examples/DropdownWithSplit.tsx @@ -7,17 +7,19 @@ import { Divider, MenuToggleElement } from '@patternfly/react-core'; +import { useState } from 'react'; export const DropdownSplitButtonText: React.FunctionComponent = () => { - const [isOpen, setIsOpen] = React.useState(false); - - const onToggleClick = () => { - setIsOpen(!isOpen); - }; + const [isOpen, setIsOpen] = useState(false); + const toggleRef = React.useRef(null); const onFocus = () => { - const element = document.getElementById('toggle-split-button-text'); - element?.focus(); + if (toggleRef.current) { + const toggleButton = toggleRef.current.querySelector('button[aria-expanded]'); + if (toggleButton) { + (toggleButton as HTMLElement).focus(); + } + } }; const onSelect = () => { @@ -25,21 +27,29 @@ export const DropdownSplitButtonText: React.FunctionComponent = () => { onFocus(); }; + const onToggleClick = () => { + setIsOpen(!isOpen); + }; + return ( setIsOpen(isOpen)} - toggle={(toggleRef: React.Ref) => ( + toggle={(toggleRefCallback: React.Ref) => ( { + // Handle both callback ref and useRef + if (typeof toggleRefCallback === 'function') { + toggleRefCallback(node); + } else if (toggleRefCallback) { + (toggleRefCallback as React.MutableRefObject).current = node; + } + (toggleRef as React.MutableRefObject).current = node; + }} splitButtonItems={[ - + ]} aria-label="Dropdown with checkbox split button" - ref={toggleRef} onClick={onToggleClick} isExpanded={isOpen} /> From 2d737dcd3d67fdeb7fc44d0190fa7cb946ad3ccd Mon Sep 17 00:00:00 2001 From: Divyanshu Gupta Date: Thu, 11 Dec 2025 22:28:52 +0530 Subject: [PATCH 07/10] remove unnecessary nesting --- .../components/Dropdown/examples/DropdownWithSplit.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/react-core/src/components/Dropdown/examples/DropdownWithSplit.tsx b/packages/react-core/src/components/Dropdown/examples/DropdownWithSplit.tsx index 3f06e1c3fce..f4abb338ec0 100644 --- a/packages/react-core/src/components/Dropdown/examples/DropdownWithSplit.tsx +++ b/packages/react-core/src/components/Dropdown/examples/DropdownWithSplit.tsx @@ -14,12 +14,12 @@ export const DropdownSplitButtonText: React.FunctionComponent = () => { const toggleRef = React.useRef(null); const onFocus = () => { - if (toggleRef.current) { - const toggleButton = toggleRef.current.querySelector('button[aria-expanded]'); - if (toggleButton) { - (toggleButton as HTMLElement).focus(); - } + if (!toggleRef.current) { + return; } + + const toggleButton = toggleRef.current.querySelector('button[aria-expanded]'); + toggleButton?.focus(); }; const onSelect = () => { From 5a7424628ebc341d7e28c7a490014e6fb870d2ea Mon Sep 17 00:00:00 2001 From: Divyanshu Gupta Date: Fri, 12 Dec 2025 01:41:55 +0530 Subject: [PATCH 08/10] update description and title --- .../react-core/src/components/Dropdown/examples/Dropdown.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-core/src/components/Dropdown/examples/Dropdown.md b/packages/react-core/src/components/Dropdown/examples/Dropdown.md index edb5d77995a..a39805d9099 100644 --- a/packages/react-core/src/components/Dropdown/examples/Dropdown.md +++ b/packages/react-core/src/components/Dropdown/examples/Dropdown.md @@ -64,9 +64,9 @@ To provide users with more context about a ``, pass a short messag ``` -### Split button +### Split toggle with checkbox -Description of split button +To combine a checkbox or other control with a dropdown menu, use a split button. ```ts file="./DropdownWithSplit.tsx" From aa7712c4f035c24d560e5aceac93b3141372dbe4 Mon Sep 17 00:00:00 2001 From: Divyanshu Gupta Date: Tue, 16 Dec 2025 23:31:09 +0530 Subject: [PATCH 09/10] update description --- .../react-core/src/components/Dropdown/examples/Dropdown.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/react-core/src/components/Dropdown/examples/Dropdown.md b/packages/react-core/src/components/Dropdown/examples/Dropdown.md index a39805d9099..b2d4b0a20c9 100644 --- a/packages/react-core/src/components/Dropdown/examples/Dropdown.md +++ b/packages/react-core/src/components/Dropdown/examples/Dropdown.md @@ -68,6 +68,10 @@ To provide users with more context about a ``, pass a short messag To combine a checkbox or other control with a dropdown menu, use a split button. +A `` can be rendered as a split button via `splitButtonItems`. Elements to be displayed before the dropdown toggle button (like the ``) must be included in the `splitButtonItems`. + +If the dropdown menu closes upon selection, you will need to manually shift focus back to the toggle element after a user selects an item from the menu. + ```ts file="./DropdownWithSplit.tsx" ``` From 0d8177e931b0f793a4c9c49583404424a19194d4 Mon Sep 17 00:00:00 2001 From: Divyanshu Gupta Date: Tue, 16 Dec 2025 23:38:17 +0530 Subject: [PATCH 10/10] update imports --- .../react-core/src/components/Dropdown/examples/Dropdown.md | 2 +- .../src/components/Dropdown/examples/DropdownWithSplit.tsx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/react-core/src/components/Dropdown/examples/Dropdown.md b/packages/react-core/src/components/Dropdown/examples/Dropdown.md index b2d4b0a20c9..4e0cf97fe46 100644 --- a/packages/react-core/src/components/Dropdown/examples/Dropdown.md +++ b/packages/react-core/src/components/Dropdown/examples/Dropdown.md @@ -16,7 +16,7 @@ propComponents: ] --- -import { useState } from 'react'; +import { useState, useRef } from 'react'; import EllipsisVIcon from '@patternfly/react-icons/dist/esm/icons/ellipsis-v-icon'; ## Examples diff --git a/packages/react-core/src/components/Dropdown/examples/DropdownWithSplit.tsx b/packages/react-core/src/components/Dropdown/examples/DropdownWithSplit.tsx index f4abb338ec0..140a99510cd 100644 --- a/packages/react-core/src/components/Dropdown/examples/DropdownWithSplit.tsx +++ b/packages/react-core/src/components/Dropdown/examples/DropdownWithSplit.tsx @@ -7,11 +7,11 @@ import { Divider, MenuToggleElement } from '@patternfly/react-core'; -import { useState } from 'react'; +import { useRef, useState } from 'react'; export const DropdownSplitButtonText: React.FunctionComponent = () => { const [isOpen, setIsOpen] = useState(false); - const toggleRef = React.useRef(null); + const toggleRef = useRef(null); const onFocus = () => { if (!toggleRef.current) {