diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5b952c6109..8abfd8c771 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,6 +19,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
### BREAKING CHANGES
- Remove convoluted conditions from component's element type calculations @kuzhelov ([#1396](https://github.com/stardust-ui/react/pull/1396))
+- Replace `Dropdown` variables: `borderRadius` with `containerBorderRadius`, `openBorderRadius` with `openAboveContainerBorderRadius` and `openBelowContainerBorderRadius`, `listBorderRadius` with `aboveListBorderRadius` and `belowListBorderRadius` @Bugaa92 ([#1312](https://github.com/stardust-ui/react/pull/1312))
### Fixes
- ESC key pressed on a trigger element should propagate event if `Popup` is closed @sophieH29 ([#1373](https://github.com/stardust-ui/react/pull/1373))
@@ -32,6 +33,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Export `message-seen`, `presence-available`, `presence-stroke`, `open-outside` and `eye-friendlier` icons to Teams theme @joheredi ([#1390](https://github.com/stardust-ui/react/pull/1390))
- Add 'lightning' icon @notandrew ([#1385](https://github.com/stardust-ui/react/pull/1385))
- Add automatic positioning inside viewport for `Menu` with submenus @Bugaa92 ([#1384](https://github.com/stardust-ui/react/pull/1384))
+- Add `align`, `position`, `offset` props for `Dropdown` component @Bugaa92 ([#1312](https://github.com/stardust-ui/react/pull/1312))
### Documentation
- Accessibility: improve introduction section @jurokapsiar ([#1368](https://github.com/stardust-ui/react/pull/1368))
diff --git a/docs/src/examples/components/Dropdown/Variations/DropdownExampleOffset.shorthand.tsx b/docs/src/examples/components/Dropdown/Variations/DropdownExampleOffset.shorthand.tsx
new file mode 100644
index 0000000000..1ad84e8708
--- /dev/null
+++ b/docs/src/examples/components/Dropdown/Variations/DropdownExampleOffset.shorthand.tsx
@@ -0,0 +1,12 @@
+import * as React from 'react'
+import { Grid, Dropdown } from '@stardust-ui/react'
+
+const inputItems = ['Bruce Wayne', 'Natasha Romanoff', 'Steven Strange', 'Alfred Pennyworth']
+
+const DropdownExamplePosition = () => (
+
+
+
+)
+
+export default DropdownExamplePosition
diff --git a/docs/src/examples/components/Dropdown/Variations/DropdownExamplePosition.shorthand.tsx b/docs/src/examples/components/Dropdown/Variations/DropdownExamplePosition.shorthand.tsx
new file mode 100644
index 0000000000..22971246ec
--- /dev/null
+++ b/docs/src/examples/components/Dropdown/Variations/DropdownExamplePosition.shorthand.tsx
@@ -0,0 +1,41 @@
+import * as React from 'react'
+import * as _ from 'lodash'
+import { Dropdown, Grid, Alignment, Position } from '@stardust-ui/react'
+import { useSelectKnob, useBooleanKnob } from '@stardust-ui/docs-components'
+
+const inputItems = ['Bruce Wayne', 'Natasha Romanoff', 'Steven Strange']
+
+const DropdownExamplePosition = () => {
+ const [open] = useBooleanKnob({ name: 'open', initialValue: true })
+
+ const [positionAndAlign] = useSelectKnob({
+ name: 'position-align',
+ initialValue: 'below',
+ values: positionAndAlignValues,
+ })
+
+ const [position, align] = _.split(positionAndAlign, '-') as [Position, Alignment]
+
+ return (
+
+
+
+ )
+}
+
+export default DropdownExamplePosition
+
+const positionAndAlignValues = [
+ 'above',
+ 'below',
+ 'before-top',
+ 'before-bottom',
+ 'after-top',
+ 'after-bottom',
+]
diff --git a/docs/src/examples/components/Dropdown/Variations/index.tsx b/docs/src/examples/components/Dropdown/Variations/index.tsx
index f9978ec1b5..52a97965ec 100644
--- a/docs/src/examples/components/Dropdown/Variations/index.tsx
+++ b/docs/src/examples/components/Dropdown/Variations/index.tsx
@@ -19,6 +19,16 @@ const Variations = () => (
description="A multiple search dropdown that uses French to provide information and accessibility."
examplePath="components/Dropdown/Variations/DropdownExampleSearchMultipleFrenchLanguage"
/>
+
+
)
diff --git a/docs/src/examples/components/Popup/Variations/PopupExamplePosition.shorthand.tsx b/docs/src/examples/components/Popup/Variations/PopupExamplePosition.shorthand.tsx
index 29c10911e8..acee076f50 100644
--- a/docs/src/examples/components/Popup/Variations/PopupExamplePosition.shorthand.tsx
+++ b/docs/src/examples/components/Popup/Variations/PopupExamplePosition.shorthand.tsx
@@ -1,54 +1,76 @@
import * as React from 'react'
-import { Button, Grid, Popup } from '@stardust-ui/react'
+import * as _ from 'lodash'
+import { Button, Grid, Popup, Alignment, Position } from '@stardust-ui/react'
+import { useBooleanKnob, useSelectKnob } from '@stardust-ui/docs-components'
-const PopupWithButton = props => {
- const { position, align, icon, padding } = props
+const PopupExamplePosition = () => {
+ const [open] = useBooleanKnob({ name: 'open-s', initialValue: true })
+
+ const [positionAndAlign] = useSelectKnob({
+ name: 'position-align-s',
+ initialValue: 'above-start',
+ values: positionAndAlignValues,
+ })
+
+ const [position, align] = _.split(positionAndAlign, '-') as [Position, Alignment]
+ const buttonStyles = { padding: paddings[positionAndAlign], height: '38px', minWidth: '64px' }
return (
- }
- content={{
- content: (
-
- The popup is rendered {position} the trigger
-
- aligned to the {align}.
-
- ),
- }}
- />
+
+ }
+ content={{
+ content: (
+
+ The popup is rendered {position} the trigger
+
+ aligned to the {align}.
+
+ ),
+ }}
+ />
+
)
}
-const triggers = [
- { position: 'above', align: 'start', icon: 'arrow circle up', padding: '5px 42px 18px 5px' },
- { position: 'above', align: 'center', icon: 'arrow circle up', padding: '5px 5px 18px 5px' },
- { position: 'above', align: 'end', icon: 'arrow circle up', padding: '5px 5px 18px 42px' },
- { position: 'below', align: 'start', icon: 'arrow circle down', padding: '18px 42px 5px 5px' },
- { position: 'below', align: 'center', icon: 'arrow circle down', padding: '18px 5px 5px 5px' },
- { position: 'below', align: 'end', icon: 'arrow circle down', padding: '18px 5px 5px 42px' },
- { position: 'before', align: 'top', icon: 'arrow circle left', padding: '5px 42px 18px 5px' },
- { position: 'before', align: 'center', icon: 'arrow circle left', padding: '5px 42px 5px 5px' },
- { position: 'before', align: 'bottom', icon: 'arrow circle left', padding: '18px 42px 5px 5px' },
- { position: 'after', align: 'top', icon: 'arrow circle right', padding: '5px 5px 18px 42px' },
- { position: 'after', align: 'center', icon: 'arrow circle right', padding: '5px 5px 5px 42px' },
- { position: 'after', align: 'bottom', icon: 'arrow circle right', padding: '18px 5px 5px 42px' },
+export default PopupExamplePosition
+
+const positionAndAlignValues = [
+ 'above-start',
+ 'above-center',
+ 'above-end',
+ 'below-start',
+ 'below-center',
+ 'below-end',
+ 'before-top',
+ 'before-center',
+ 'before-bottom',
+ 'after-top',
+ 'after-center',
+ 'after-bottom',
]
-const PopupExamplePosition = () => (
-
- {triggers.map(({ position, align, icon, padding }) => (
-
- ))}
-
-)
+const icons: Record = {
+ above: 'arrow circle up',
+ below: 'arrow circle down',
+ before: 'arrow circle left',
+ after: 'arrow circle right',
+}
-export default PopupExamplePosition
+const paddings: Record = {
+ 'above-start': '5px 42px 18px 5px',
+ 'above-center': '5px 5px 18px 5px',
+ 'above-end': '5px 5px 18px 42px',
+ 'below-start': '18px 42px 5px 5px',
+ 'below-center': '18px 5px 5px 5px',
+ 'below-end': '18px 5px 5px 42px',
+ 'before-top': '5px 42px 18px 5px',
+ 'before-center': '5px 42px 5px 5px',
+ 'before-bottom': '18px 42px 5px 5px',
+ 'after-top': '5px 5px 18px 42px',
+ 'after-center': '5px 5px 5px 42px',
+ 'after-bottom': '18px 5px 5px 42px',
+}
diff --git a/docs/src/examples/components/Popup/Variations/PopupExamplePosition.tsx b/docs/src/examples/components/Popup/Variations/PopupExamplePosition.tsx
index bbce0f5ee8..fb6f7d86f6 100644
--- a/docs/src/examples/components/Popup/Variations/PopupExamplePosition.tsx
+++ b/docs/src/examples/components/Popup/Variations/PopupExamplePosition.tsx
@@ -1,57 +1,77 @@
import * as React from 'react'
-import { Button, Grid, Popup } from '@stardust-ui/react'
+import * as _ from 'lodash'
+import { Button, Grid, Popup, Alignment, Position } from '@stardust-ui/react'
+import { useBooleanKnob, useSelectKnob } from '@stardust-ui/docs-components'
-const PopupArrowExample = props => {
- const { position, align, icon, padding } = props
+const PopupExamplePosition = () => {
+ const [open] = useBooleanKnob({ name: 'open-c', initialValue: true })
- const buttonStyles = { padding, height: '38px', minWidth: '64px' }
+ const [positionAndAlign] = useSelectKnob({
+ name: 'position-align-c',
+ initialValue: 'above-start',
+ values: positionAndAlignValues,
+ })
+
+ const [position, align] = _.split(positionAndAlign, '-') as [Position, Alignment]
+ const buttonStyles = { padding: paddings[positionAndAlign], height: '38px', minWidth: '64px' }
return (
-
- The popup is rendered {position} the trigger
-
- aligned to the {align}.
-
- ),
- }}
- >
-
-
+
+
+ The popup is rendered {position} the trigger
+
+ aligned to the {align}.
+
+ ),
+ }}
+ >
+
+
+
)
}
-const triggers = [
- { position: 'above', align: 'start', icon: 'arrow circle up', padding: '5px 42px 18px 5px' },
- { position: 'above', align: 'center', icon: 'arrow circle up', padding: '5px 5px 18px 5px' },
- { position: 'above', align: 'end', icon: 'arrow circle up', padding: '5px 5px 18px 42px' },
- { position: 'below', align: 'start', icon: 'arrow circle down', padding: '18px 42px 5px 5px' },
- { position: 'below', align: 'center', icon: 'arrow circle down', padding: '18px 5px 5px 5px' },
- { position: 'below', align: 'end', icon: 'arrow circle down', padding: '18px 5px 5px 42px' },
- { position: 'before', align: 'top', icon: 'arrow circle left', padding: '5px 42px 18px 5px' },
- { position: 'before', align: 'center', icon: 'arrow circle left', padding: '5px 42px 5px 5px' },
- { position: 'before', align: 'bottom', icon: 'arrow circle left', padding: '18px 42px 5px 5px' },
- { position: 'after', align: 'top', icon: 'arrow circle right', padding: '5px 5px 18px 42px' },
- { position: 'after', align: 'center', icon: 'arrow circle right', padding: '5px 5px 5px 42px' },
- { position: 'after', align: 'bottom', icon: 'arrow circle right', padding: '18px 5px 5px 42px' },
+export default PopupExamplePosition
+
+const positionAndAlignValues = [
+ 'above-start',
+ 'above-center',
+ 'above-end',
+ 'below-start',
+ 'below-center',
+ 'below-end',
+ 'before-top',
+ 'before-center',
+ 'before-bottom',
+ 'after-top',
+ 'after-center',
+ 'after-bottom',
]
-const PopupExamplePosition = () => (
-
- {triggers.map(({ position, align, icon, padding }) => (
-
- ))}
-
-)
+const icons: Record = {
+ above: 'arrow circle up',
+ below: 'arrow circle down',
+ before: 'arrow circle left',
+ after: 'arrow circle right',
+}
-export default PopupExamplePosition
+const paddings: Record = {
+ 'above-start': '5px 42px 18px 5px',
+ 'above-center': '5px 5px 18px 5px',
+ 'above-end': '5px 5px 18px 42px',
+ 'below-start': '18px 42px 5px 5px',
+ 'below-center': '18px 5px 5px 5px',
+ 'below-end': '18px 5px 5px 42px',
+ 'before-top': '5px 42px 18px 5px',
+ 'before-center': '5px 42px 5px 5px',
+ 'before-bottom': '18px 42px 5px 5px',
+ 'after-top': '5px 5px 18px 42px',
+ 'after-center': '5px 5px 5px 42px',
+ 'after-bottom': '18px 5px 5px 42px',
+}
diff --git a/docs/src/prototypes/mentions/MentionsDropdown.tsx b/docs/src/prototypes/mentions/MentionsDropdown.tsx
index 3913c1ebb4..51eca93f86 100644
--- a/docs/src/prototypes/mentions/MentionsDropdown.tsx
+++ b/docs/src/prototypes/mentions/MentionsDropdown.tsx
@@ -22,9 +22,10 @@ const MentionsDropdown: React.FunctionComponent = props
(
{
+export interface DropdownProps
+ extends UIComponentProps,
+ PositioningProps {
/** The index of the currently active selected item, if dropdown has a multiple selection. */
activeSelectedIndex?: number
@@ -221,6 +224,7 @@ class Dropdown extends AutoControlledComponent, Dropdo
private inputRef = React.createRef()
private listRef = React.createRef()
private selectedItemsRef = React.createRef()
+ private containerRef = React.createRef()
static displayName = 'Dropdown'
@@ -238,6 +242,7 @@ class Dropdown extends AutoControlledComponent, Dropdo
content: false,
}),
activeSelectedIndex: PropTypes.number,
+ align: PropTypes.oneOf(ALIGNMENTS),
clearable: PropTypes.bool,
clearIndicator: customPropTypes.itemShorthand,
defaultActiveSelectedIndex: PropTypes.number,
@@ -261,11 +266,13 @@ class Dropdown extends AutoControlledComponent, Dropdo
moveFocusOnTab: PropTypes.bool,
multiple: PropTypes.bool,
noResultsMessage: customPropTypes.itemShorthand,
+ offset: PropTypes.string,
onOpenChange: PropTypes.func,
onSearchQueryChange: PropTypes.func,
onSelectedChange: PropTypes.func,
open: PropTypes.bool,
placeholder: PropTypes.string,
+ position: PropTypes.oneOf(POSITIONS),
renderItem: PropTypes.func,
renderSelectedItem: PropTypes.func,
search: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]),
@@ -280,6 +287,7 @@ class Dropdown extends AutoControlledComponent, Dropdo
}
static defaultProps = {
+ align: 'start',
as: 'div',
clearIndicator: 'stardust-close',
itemToString: item => {
@@ -290,6 +298,7 @@ class Dropdown extends AutoControlledComponent, Dropdo
// targets DropdownItem shorthand objects
return (item as any).header || String(item)
},
+ position: 'below',
toggleIndicator: {},
triggerButton: {},
}
@@ -425,6 +434,7 @@ class Dropdown extends AutoControlledComponent, Dropdo
return (
[
]
@@ -482,6 +492,7 @@ class Dropdown extends AutoControlledComponent, Dropdo
getItemProps,
getInputProps,
value,
+ rtl,
)}
@@ -602,9 +613,13 @@ class Dropdown extends AutoControlledComponent, Dropdo
getItemProps: (options: GetItemPropsOptions) => any,
getInputProps: (options?: GetInputPropsOptions) => any,
value: ShorthandValue | ShorthandCollection,
+ rtl: boolean,
) {
- const { search } = this.props
+ const { align, offset, position, search } = this.props
const { open } = this.state
+ const items = open
+ ? this.renderItems(styles, variables, getItemProps, highlightedIndex, value)
+ : []
const { innerRef, ...accessibilityMenuProps } = getMenuProps(
{ refKey: 'innerRef' },
{ suppressRefError: true },
@@ -634,18 +649,26 @@ class Dropdown extends AutoControlledComponent, Dropdo
handleRef(innerRef, listElement)
}}
>
-
+
+
+
)
}
diff --git a/packages/react/src/lib/positioner/Popper.tsx b/packages/react/src/lib/positioner/Popper.tsx
index 4ef2da4f5d..0cd54ec2f0 100644
--- a/packages/react/src/lib/positioner/Popper.tsx
+++ b/packages/react/src/lib/positioner/Popper.tsx
@@ -49,6 +49,8 @@ const Popper: React.FunctionComponent = props => {
const popperHasScrollableParent = getScrollParent(contentRef.current) !== document.body
const modifiers: PopperJS.Modifiers = _.merge(
+ { preventOverflow: { padding: 0 } },
+ { flip: { padding: 0 } },
/**
* When the popper box is placed in the context of a scrollable element, we need to set
* preventOverflow.escapeWithReference to true and flip.boundariesElement to 'scrollParent' (default is 'viewport')
diff --git a/packages/react/src/lib/positioner/positioningHelper.ts b/packages/react/src/lib/positioner/positioningHelper.ts
index b128a7d7a3..c14a7e042a 100644
--- a/packages/react/src/lib/positioner/positioningHelper.ts
+++ b/packages/react/src/lib/positioner/positioningHelper.ts
@@ -12,14 +12,14 @@ enum PlacementParts {
center = '',
}
-const getPositionMap = (rtl: boolean): { [key in Position]: PlacementParts } => ({
+const getPositionMap = (rtl: boolean): Record => ({
above: PlacementParts.top,
below: PlacementParts.bottom,
before: rtl ? PlacementParts.right : PlacementParts.left,
after: rtl ? PlacementParts.left : PlacementParts.right,
})
-const getAlignmentMap = (rtl: boolean): { [key in Alignment]: PlacementParts } => ({
+const getAlignmentMap = (rtl: boolean): Record => ({
start: rtl ? PlacementParts.end : PlacementParts.start,
end: rtl ? PlacementParts.start : PlacementParts.end,
top: PlacementParts.start,
diff --git a/packages/react/src/themes/teams-high-contrast/components/Dropdown/dropdownVariables.ts b/packages/react/src/themes/teams-high-contrast/components/Dropdown/dropdownVariables.ts
index ae9af09c85..9738d0c45a 100644
--- a/packages/react/src/themes/teams-high-contrast/components/Dropdown/dropdownVariables.ts
+++ b/packages/react/src/themes/teams-high-contrast/components/Dropdown/dropdownVariables.ts
@@ -5,16 +5,20 @@ export interface DropdownVariablesHC extends DropdownVariables {
borderColorHover: string
}
+const _3px_asRem = pxToRem(3)
+
export default (siteVars): Partial => ({
backgroundColor: siteVars.colors.black,
borderColor: siteVars.colors.white,
- borderRadius: pxToRem(3),
+ containerBorderRadius: _3px_asRem,
+ openAboveContainerBorderRadius: `0 0 ${_3px_asRem} ${_3px_asRem}`,
borderWidth: `1px`,
backgroundColorHover: siteVars.colors.black,
borderColorHover: siteVars.accessibleYellow,
borderColorFocus: siteVars.accessibleCyan,
color: siteVars.colors.white,
selectedItemColor: siteVars.colors.white,
+ belowListBorderRadius: `0 0 ${_3px_asRem} ${_3px_asRem}`,
listBackgroundColor: siteVars.colors.black,
listBorderColor: siteVars.colors.white,
listBoxShadow: undefined,
diff --git a/packages/react/src/themes/teams/components/Dropdown/dropdownStyles.ts b/packages/react/src/themes/teams/components/Dropdown/dropdownStyles.ts
index 69108e0f5f..d4d29055b8 100644
--- a/packages/react/src/themes/teams/components/Dropdown/dropdownStyles.ts
+++ b/packages/react/src/themes/teams/components/Dropdown/dropdownStyles.ts
@@ -56,15 +56,9 @@ const getWidth = (p: DropdownPropsAndState, v: DropdownVariables): string => {
return v.width
}
-const getContainerBorderRadius = (p, v) => {
- return p.open ? v.openBorderRadius : v.borderRadius
-}
-
const dropdownStyles: ComponentSlotStylesInput = {
root: ({ props: p }): ICSSInJSStyle => ({
- ...(p.inline && {
- display: 'inline-flex',
- }),
+ ...(p.inline && { display: 'inline-flex' }),
}),
clearIndicator: getIndicatorStyles,
@@ -78,9 +72,11 @@ const dropdownStyles: ComponentSlotStylesInput ({
outline: 0,
- position: 'absolute',
- borderRadius: v.listBorderRadius,
borderStyle: 'solid',
borderWidth: p.open ? v.listBorderWidth : '0px',
borderColor: v.listBorderColor,
@@ -153,9 +146,9 @@ const dropdownStyles: ComponentSlotStylesInput ({
backgroundColorHover: siteVars.colors.grey[150],
borderColor: 'transparent',
borderColorFocus: siteVars.colors.brand[600],
- borderRadius: `${_3px_asRem} ${_3px_asRem} ${_2px_asRem} ${_2px_asRem}`,
- openBorderRadius: `${_3px_asRem} ${_3px_asRem} 0 0`,
borderWidth: '0px',
+ containerBorderRadius: `${_3px_asRem} ${_3px_asRem} ${_2px_asRem} ${_2px_asRem}`,
+ openAboveContainerBorderRadius: `0 0 ${_2px_asRem} ${_2px_asRem}`,
+ openBelowContainerBorderRadius: `${_3px_asRem} ${_3px_asRem} 0 0`,
searchBorderBottomWidth: pxToRem(2),
color: siteVars.bodyColor,
selectedItemColor: siteVars.bodyColor,
comboboxPaddingButton: `0 ${_12px_asRem}`,
comboboxFlexBasis: pxToRem(50),
+ aboveListBorderRadius: `${_3px_asRem} ${_3px_asRem} 0 0`,
+ belowListBorderRadius: `0 0 ${_2px_asRem} ${_2px_asRem}`,
listBackgroundColor: siteVars.colors.white,
- listBorderRadius: `0 0 ${_3px_asRem} ${_3px_asRem}`,
listBorderColor: 'transparent',
listBorderWidth: '0px',
listPadding: `${pxToRem(8)} 0`,