diff --git a/src/components/Panel/styles.css b/src/components/Panel/styles.css
index a47d7c358..253584153 100644
--- a/src/components/Panel/styles.css
+++ b/src/components/Panel/styles.css
@@ -18,7 +18,19 @@
cursor: pointer;
font-weight: $font-weight-bold;
border-bottom: 1px solid $color-border-base;
+ border: unset;
+ outline: none;
+ background-color: unset;
+ width: 100%;
+ text-align: left;
+ border-radius: inherit;
line-height: 22px;
+
+ &:focus-visible {
+ outline: none;
+ border-color: $color-white;
+ box-shadow: 0 0 0 2px $color-grey-700;
+ }
}
.panel-component-header::before {
diff --git a/src/components/Radio/index.jsx b/src/components/Radio/index.jsx
index f54f10689..396b318b5 100644
--- a/src/components/Radio/index.jsx
+++ b/src/components/Radio/index.jsx
@@ -42,9 +42,9 @@ const Radio = ({
return (
{
invariant(!inline, 'RadioGroup: the inline prop has been replaced by orientation="vertical"');
@@ -42,6 +47,7 @@ const RadioGroup = ({
useArrowFocus({
ref,
+ disabled: disableArrowKeys,
onFocus: (el) => onChange(el.dataset.auiValue),
selector: `.${itemClass}[role=radio]`,
loop: true,
@@ -68,7 +74,7 @@ const RadioGroup = ({
);
};
-RadioGroup.propTypes = {
+export const radioGroupPropTypes = {
value: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
@@ -84,4 +90,8 @@ RadioGroup.propTypes = {
inline: PropTypes.bool,
};
+RadioGroup.propTypes = {
+ ...radioGroupPropTypes,
+};
+
export default RadioGroup;
diff --git a/src/index.d.ts b/src/index.d.ts
index 964ad3fda..d92ac0da7 100644
--- a/src/index.d.ts
+++ b/src/index.d.ts
@@ -19,12 +19,14 @@ export { default as Empty } from './components/Empty';
export { default as fastStatelessWrapper } from './components/fastStatelessWrapper';
export { default as FilePicker } from './components/FilePicker';
export { default as FlexibleSpacer } from './components/FlexibleSpacer';
+export { default as DismissableFocusTrap } from './components/DismissibleFocusTrap';
export { default as FormGroup } from './components/FormGroup';
export { default as Grid } from './components/Grid';
export { default as GridCell } from './components/Grid/Cell';
export { default as GridRow } from './components/Grid/Row';
export { default as HelpIconPopover } from './components/HelpIconPopover';
export { default as HoverDropdownMenu } from './components/HoverDropdownMenu';
+export { default as DropdownMenu } from './components/DropdownMenu';
export { default as ImageCropper } from './components/ImageCropper';
export { default as InformationBox } from './components/InformationBox';
export { default as ListPicker } from './components/ListPicker';
diff --git a/src/index.js b/src/index.js
index cf64bfa0f..152f83dd7 100644
--- a/src/index.js
+++ b/src/index.js
@@ -23,12 +23,14 @@ import Empty from './components/Empty';
import fastStatelessWrapper from './components/fastStatelessWrapper';
import FilePicker from './components/FilePicker';
import FlexibleSpacer from './components/FlexibleSpacer';
+import DismissibleFocusTrap from './components/DismissibleFocusTrap';
import FormGroup from './components/FormGroup';
import Grid from './components/Grid';
import GridCell from './components/Grid/Cell';
import GridRow from './components/Grid/Row';
import HelpIconPopover from './components/HelpIconPopover';
import HoverDropdownMenu from './components/HoverDropdownMenu';
+import DropdownMenu from './components/DropdownMenu';
import ImageCropper from './components/ImageCropper';
import InformationBox from './components/InformationBox';
import ListPicker from './components/ListPicker';
@@ -41,7 +43,7 @@ import Pagination from './components/Pagination';
import Panel from './components/Panel';
import Paragraph from './components/Paragraph';
import Pill from './components/Pill';
-import Popover from './components/Popover';
+import Popover, { usePopover } from './components/Popover';
import PrettyDiff from './components/PrettyDiff';
import Radio from './components/Radio';
import RadioGroup from './components/RadioGroup';
@@ -97,6 +99,7 @@ export {
fastStatelessWrapper,
FilePicker,
FlexibleSpacer,
+ DismissibleFocusTrap,
FormGroup,
Grid,
GridCell,
@@ -111,6 +114,7 @@ export {
Pagination,
Panel,
Popover,
+ usePopover,
PrettyDiff,
Radio,
RadioGroup,
@@ -139,6 +143,7 @@ export {
InformationBox,
ImageCropper,
HoverDropdownMenu,
+ DropdownMenu,
OverlayLoader,
VerticalNav,
RichTextEditor,
diff --git a/www/containers/props.json b/www/containers/props.json
index e95a50347..0ed1ed839 100644
--- a/www/containers/props.json
+++ b/www/containers/props.json
@@ -793,6 +793,23 @@
"required": false,
"description": ""
},
+ "iconPosition": {
+ "type": {
+ "name": "enum",
+ "value": [
+ {
+ "value": "'left'",
+ "computed": false
+ },
+ {
+ "value": "'right'",
+ "computed": false
+ }
+ ]
+ },
+ "required": false,
+ "description": ""
+ },
"theme": {
"type": {
"name": "string"
@@ -1447,14 +1464,14 @@
"type": {
"name": "array"
},
- "required": false,
+ "required": true,
"description": ""
},
"name": {
"type": {
"name": "string"
},
- "required": false,
+ "required": true,
"description": ""
},
"onChange": {
@@ -1770,7 +1787,7 @@
"name": "func"
},
"required": false,
- "description": ""
+ "description": "useful if a menu/popover should be closed after tabbing through it"
},
"onShiftTabExit": {
"type": {
@@ -1789,6 +1806,634 @@
}
}
],
+ "src/components/DropdownMenu/index.jsx": [
+ {
+ "description": "",
+ "displayName": "DropdownMenuProvider",
+ "methods": []
+ },
+ {
+ "description": "",
+ "displayName": "DropdownMenu",
+ "methods": [
+ {
+ "name": "Content",
+ "docblock": null,
+ "modifiers": [
+ "static"
+ ],
+ "params": [
+ {
+ "name": "{ children, className, placement, modifiers, dts }",
+ "type": null
+ }
+ ],
+ "returns": null
+ },
+ {
+ "name": "Trigger",
+ "docblock": null,
+ "modifiers": [
+ "static"
+ ],
+ "params": [
+ {
+ "name": "{ disabled = false, className, children, icon, ...rest }",
+ "type": null
+ }
+ ],
+ "returns": null
+ },
+ {
+ "name": "Item",
+ "docblock": null,
+ "modifiers": [
+ "static"
+ ],
+ "params": [
+ {
+ "name": "{ children, onClick, disabled, className, dts, ...rest }",
+ "type": null
+ }
+ ],
+ "returns": null
+ },
+ {
+ "name": "CheckboxGroup",
+ "docblock": null,
+ "modifiers": [
+ "static"
+ ],
+ "params": [
+ {
+ "name": "{ children, onChange, className, ...rest }",
+ "type": null
+ }
+ ],
+ "returns": null
+ },
+ {
+ "name": "Checkbox",
+ "docblock": null,
+ "modifiers": [
+ "static"
+ ],
+ "params": [
+ {
+ "name": "{ children, className, ...rest }",
+ "type": null
+ }
+ ],
+ "returns": null
+ },
+ {
+ "name": "CheckboxAll",
+ "docblock": null,
+ "modifiers": [
+ "static"
+ ],
+ "params": [
+ {
+ "name": "{ children, className, ...rest }",
+ "type": null
+ }
+ ],
+ "returns": null
+ },
+ {
+ "name": "RadioGroup",
+ "docblock": null,
+ "modifiers": [
+ "static"
+ ],
+ "params": [
+ {
+ "name": "{ children, onChange, className, ...rest }",
+ "type": null
+ }
+ ],
+ "returns": null
+ },
+ {
+ "name": "Radio",
+ "docblock": null,
+ "modifiers": [
+ "static"
+ ],
+ "params": [
+ {
+ "name": "{ children, onClick, className, ...rest }",
+ "type": null
+ }
+ ],
+ "returns": null
+ },
+ {
+ "name": "Label",
+ "docblock": null,
+ "modifiers": [
+ "static"
+ ],
+ "params": [
+ {
+ "name": "{ children, className, ...rest }",
+ "type": null
+ }
+ ],
+ "returns": null
+ },
+ {
+ "name": "ItemContainer",
+ "docblock": null,
+ "modifiers": [
+ "static"
+ ],
+ "params": [
+ {
+ "name": "{ children, className, ...rest }",
+ "type": null
+ }
+ ],
+ "returns": null
+ },
+ {
+ "name": "Divider",
+ "docblock": null,
+ "modifiers": [
+ "static"
+ ],
+ "params": [
+ {
+ "name": "{ className }",
+ "type": null
+ }
+ ],
+ "returns": null
+ },
+ {
+ "name": "Group",
+ "docblock": null,
+ "modifiers": [
+ "static"
+ ],
+ "params": [
+ {
+ "name": "{ children, className, defaultCollapsed, collapsible, title, id, ...rest }",
+ "type": null
+ }
+ ],
+ "returns": null
+ },
+ {
+ "name": "useDropdownMenu",
+ "docblock": null,
+ "modifiers": [
+ "static"
+ ],
+ "params": [],
+ "returns": null
+ }
+ ],
+ "props": {
+ "defaultOpen": {
+ "type": {
+ "name": "bool"
+ },
+ "required": false,
+ "description": "Initial open state. Can be used to toggle the open state programatically."
+ },
+ "closeOnItemClick": {
+ "type": {
+ "name": "bool"
+ },
+ "required": false,
+ "description": "Closes the menu when an item with an onClick handler is clicked.\nAlso applies to checkboxes and radios."
+ },
+ "onOpen": {
+ "type": {
+ "name": "func"
+ },
+ "required": false,
+ "description": ""
+ },
+ "onClose": {
+ "type": {
+ "name": "func"
+ },
+ "required": false,
+ "description": ""
+ },
+ "submenu": {
+ "type": {
+ "name": "bool"
+ },
+ "required": false,
+ "description": "opt-in to submenu behaviour for nested menus"
+ },
+ "triggerRef": {
+ "type": {
+ "name": "object"
+ },
+ "required": false,
+ "description": "Optional ref to mount the dropdown to\n\n*Only use when using Dropdown.Trigger is not feasible*"
+ },
+ "triggerId": {
+ "type": {
+ "name": "string"
+ },
+ "required": false,
+ "description": "A unique trigger id is required for accessiblilty purposes"
+ },
+ "contentId": {
+ "type": {
+ "name": "string"
+ },
+ "required": false,
+ "description": "A unique content id is required for accessiblilty purposes"
+ },
+ "children": {
+ "type": {
+ "name": "union",
+ "value": [
+ {
+ "name": "node"
+ },
+ {
+ "name": "func"
+ }
+ ]
+ },
+ "required": false,
+ "description": "A render function may be used, which receives the dropdown context.\nNotably: `open` state, `closeMenu()` function, `triggerRef`, `contentRef`."
+ }
+ }
+ },
+ {
+ "description": "",
+ "displayName": "DropdownMenuTrigger",
+ "methods": [],
+ "props": {
+ "isLoading": {
+ "type": {
+ "name": "bool"
+ },
+ "required": false,
+ "description": ""
+ },
+ "color": {
+ "type": {
+ "name": "enum",
+ "computed": true,
+ "value": "colors"
+ },
+ "required": false,
+ "description": ""
+ },
+ "variant": {
+ "type": {
+ "name": "enum",
+ "computed": true,
+ "value": "variants"
+ },
+ "required": false,
+ "description": ""
+ },
+ "size": {
+ "type": {
+ "name": "enum",
+ "computed": true,
+ "value": "sizes"
+ },
+ "required": false,
+ "description": ""
+ },
+ "iconPosition": {
+ "type": {
+ "name": "enum",
+ "computed": true,
+ "value": "positions"
+ },
+ "required": false,
+ "description": ""
+ },
+ "icon": {
+ "type": {
+ "name": "node"
+ },
+ "required": false,
+ "description": ""
+ },
+ "fullWidth": {
+ "type": {
+ "name": "bool"
+ },
+ "required": false,
+ "description": ""
+ },
+ "className": {
+ "type": {
+ "name": "string"
+ },
+ "required": false,
+ "description": ""
+ },
+ "dts": {
+ "type": {
+ "name": "string"
+ },
+ "required": false,
+ "description": ""
+ },
+ "disabled": {
+ "type": {
+ "name": "bool"
+ },
+ "required": false,
+ "description": "",
+ "defaultValue": {
+ "value": "false",
+ "computed": false
+ }
+ },
+ "children": {
+ "type": {
+ "name": "node"
+ },
+ "required": false,
+ "description": ""
+ }
+ }
+ },
+ {
+ "description": "ths is a separate component to cater for Popover `popoverContent`'s\nrender function and regular function component prop",
+ "displayName": "INNER_CONTENT",
+ "methods": []
+ },
+ {
+ "description": "Dropdown Menu Content container\nAny menu items should be children of this component",
+ "displayName": "DropdownMenuContent",
+ "methods": [],
+ "props": {
+ "children": {
+ "type": {
+ "name": "union",
+ "value": [
+ {
+ "name": "func"
+ },
+ {
+ "name": "node"
+ }
+ ]
+ },
+ "required": false,
+ "description": ""
+ },
+ "className": {
+ "type": {
+ "name": "string"
+ },
+ "required": false,
+ "description": ""
+ },
+ "dts": {
+ "type": {
+ "name": "string"
+ },
+ "required": false,
+ "description": ""
+ },
+ "id": {
+ "type": {
+ "name": "string"
+ },
+ "required": false,
+ "description": ""
+ },
+ "placement": {
+ "type": {
+ "name": "enum",
+ "computed": true,
+ "value": "popoverPlacements"
+ },
+ "required": false,
+ "description": ""
+ },
+ "modifiers": {
+ "type": {
+ "name": "union",
+ "value": [
+ {
+ "name": "object"
+ },
+ {
+ "name": "arrayOf",
+ "value": {
+ "name": "object"
+ }
+ }
+ ]
+ },
+ "required": false,
+ "description": ""
+ }
+ }
+ },
+ {
+ "description": "",
+ "displayName": "DropdownMenuItem",
+ "methods": [],
+ "props": {
+ "children": {
+ "type": {
+ "name": "node"
+ },
+ "required": false,
+ "description": ""
+ },
+ "disabled": {
+ "type": {
+ "name": "bool"
+ },
+ "required": false,
+ "description": ""
+ },
+ "icon": {
+ "type": {
+ "name": "node"
+ },
+ "required": false,
+ "description": ""
+ },
+ "className": {
+ "type": {
+ "name": "string"
+ },
+ "required": false,
+ "description": ""
+ },
+ "dts": {
+ "type": {
+ "name": "string"
+ },
+ "required": false,
+ "description": ""
+ },
+ "onClick": {
+ "type": {
+ "name": "func"
+ },
+ "required": false,
+ "description": ""
+ }
+ }
+ },
+ {
+ "description": "",
+ "displayName": "DropdownMenuCheckboxGroup",
+ "methods": [],
+ "composes": [
+ "../CheckboxGroup"
+ ]
+ },
+ {
+ "description": "",
+ "displayName": "DropdownMenuCheckbox",
+ "methods": [],
+ "composes": [
+ "../Checkbox"
+ ]
+ },
+ {
+ "description": "",
+ "displayName": "DropdownMenuCheckboxAll",
+ "methods": [],
+ "composes": [
+ "../CheckboxGroup"
+ ]
+ },
+ {
+ "description": "",
+ "displayName": "DropdownMenuRadioGroup",
+ "methods": [],
+ "composes": [
+ "../RadioGroup"
+ ]
+ },
+ {
+ "description": "",
+ "displayName": "DropdownMenuRadio",
+ "methods": [],
+ "composes": [
+ "../Radio"
+ ]
+ },
+ {
+ "description": "",
+ "displayName": "DropdownMenuLabel",
+ "methods": [],
+ "props": {
+ "className": {
+ "type": {
+ "name": "string"
+ },
+ "required": false,
+ "description": ""
+ },
+ "children": {
+ "type": {
+ "name": "node"
+ },
+ "required": false,
+ "description": ""
+ }
+ }
+ },
+ {
+ "description": "",
+ "displayName": "DropdownMenuDivider",
+ "methods": [],
+ "props": {
+ "className": {
+ "type": {
+ "name": "string"
+ },
+ "required": false,
+ "description": ""
+ }
+ }
+ },
+ {
+ "description": "a div Styled like a `DropdownMenu.Item`",
+ "displayName": "DropdownMenuItemContainer",
+ "methods": [],
+ "props": {
+ "className": {
+ "type": {
+ "name": "string"
+ },
+ "required": false,
+ "description": ""
+ },
+ "children": {
+ "type": {
+ "name": "node"
+ },
+ "required": false,
+ "description": ""
+ }
+ }
+ },
+ {
+ "description": "",
+ "displayName": "DropdownMenuGroup",
+ "methods": [],
+ "props": {
+ "className": {
+ "type": {
+ "name": "string"
+ },
+ "required": false,
+ "description": ""
+ },
+ "children": {
+ "type": {
+ "name": "node"
+ },
+ "required": false,
+ "description": ""
+ },
+ "id": {
+ "type": {
+ "name": "string"
+ },
+ "required": false,
+ "description": ""
+ },
+ "collapsible": {
+ "type": {
+ "name": "bool"
+ },
+ "required": false,
+ "description": "Renders the group as a collapsible panel component"
+ },
+ "defaultCollapsed": {
+ "type": {
+ "name": "bool"
+ },
+ "required": false,
+ "description": ""
+ },
+ "title": {
+ "type": {
+ "name": "string"
+ },
+ "required": false,
+ "description": "The group's heading.\nTitle must be used if collapsible is true"
+ }
+ }
+ }
+ ],
"src/components/Empty/index.jsx": [
{
"description": "",
diff --git a/www/containers/routes.js b/www/containers/routes.js
index 361f3e257..995ff203d 100644
--- a/www/containers/routes.js
+++ b/www/containers/routes.js
@@ -24,6 +24,7 @@ import GridExample from '../examples/Grid.mdx';
import HelpIconPopoverExample from '../examples/HelpIconPopover.mdx';
import PrettyDiffExample from '../examples/PrettyDiff.mdx';
import HoverDropdownMenuExample from '../examples/HoverDropdownMenu.mdx';
+import DropdownMenuExample from '../examples/DropdownMenu.mdx';
import InformationBoxExample from '../examples/InformationBox.mdx';
import ImageCropperExample from '../examples/ImageCropper.mdx';
import ListPickerExample from '../examples/ListPicker.mdx';
@@ -258,6 +259,12 @@ const routes = [
title: 'Hover Dropdown Menu',
group: 'Components',
},
+ {
+ path: '/dropdown-menu',
+ component: DropdownMenuExample,
+ title: 'Dropdown Menu',
+ group: 'Components',
+ },
{
path: '/information-box',
component: InformationBoxExample,
diff --git a/www/examples/ButtonGroup.mdx b/www/examples/ButtonGroup.mdx
index 0a8cdfb9f..259736f35 100644
--- a/www/examples/ButtonGroup.mdx
+++ b/www/examples/ButtonGroup.mdx
@@ -11,13 +11,16 @@ const Example = () => {
Approve
-
-
+ }
/>
-
+
+ console.log('click')}>Option One
+ Option Two
+
+
Reject
diff --git a/www/examples/DropdownMenu.mdx b/www/examples/DropdownMenu.mdx
new file mode 100644
index 000000000..fbba8cabf
--- /dev/null
+++ b/www/examples/DropdownMenu.mdx
@@ -0,0 +1,250 @@
+import Props from '../containers/Props.jsx';
+import DesignNotes from '../containers/DesignNotes.jsx';
+
+## Dropdown Menu
+
+```jsx live=true
+const UserMenuItem = (
+
+ John Smith
+
+
+
+
+
+ }
+ aria-label="message"
+ />
+
+
+
+
+
+
+
+ }
+ aria-label="menu"
+ />
+
+
+ Action 1
+ Action 2
+ Action 3
+
+
+
+
+
+);
+const Example = () => {
+ const [state, setState] = React.useState();
+ return (
+ <>
+
+ Manage Users
+
+
+ Admins
+ {UserMenuItem}
+ {UserMenuItem}
+
+
+ Users
+ {UserMenuItem}
+ {UserMenuItem}
+
+
+
+
+
+ Menu
+
+
+ setState('Item 1')}>Item 1
+ setState('Item 2')}>Item 2
+ setState('Item 3')}>Item 3
+
+
+
+ Sub Menu A
+
+ setState('Sub Item A1')}>Sub Item A1
+
+
+ Sub Menu B
+
+ setState('Sub Item B1')}>Sub Item B1
+
+
+ Sub Menu C
+
+ setState('Sub Item C1')}>Sub Item C1
+
+
+
+
+
+
+
+
+ You chose: {state}
+ >
+ );
+};
+
+render(Example);
+```
+
+## Dropdown Menu Checkbox and Radio groups
+
+This example shows how to use Checkbox and Radio groups within a menu
+
+```jsx live=true
+const Example = () => {
+ const [radio, setRadio] = React.useState('block');
+ const [checkbox, setCheckbox] = React.useState([]);
+ const [closeOnClick, setCloseOnClick] = React.useState(false);
+
+ return (
+ <>
+ Close after selection
+ setCloseOnClick(v)} />
+
+
+ Radio: {radio}
+
+ Checkbox: {checkbox.map((v) => v).join(', ')}
+
+
+ {({ closeMenu }) => (
+ <>
+
+ Menu
+
+
+ Elements
+
+
+
+
+
+
+
+ Display
+
+
+
+
+
+
+
+ Cancel
+
+
+ Done
+
+
+
+ >
+ )}
+
+ >
+ );
+};
+
+render(Example);
+```
+
+## Dropdown Menu Collapsible Groups
+
+This example shows the menu with a lot of items causing scroll overflow, as well as the sticky group headers
+
+```jsx live=true
+const Example = () => {
+ return (
+ <>
+
+ Menu
+
+
+ Item 1
+ Item 2
+ Item 3
+ Item 1
+ Item 2
+ Item 3
+
+
+ Item 2
+ Item 3
+ Item 2
+ Item 3
+ Item 2
+ Item 3
+ Item 2
+ Item 3
+ Item 2
+ Item 3
+ Item 2
+ Item 3
+ Item 2
+ Item 3
+ Item 2
+ Item 3
+ Item 2
+ Item 3
+
+
+
+ >
+ );
+};
+
+render(Example);
+```
+
+### Design Notes
+
+
+
+
Dropdown menu aids in helping the user discover selectable options.
+
Example: Profile menu
+
+
+