diff --git a/packages/web-react/src/components/Dropdown/Dropdown.tsx b/packages/web-react/src/components/Dropdown/Dropdown.tsx
index 400b6358b4..8dd199545b 100644
--- a/packages/web-react/src/components/Dropdown/Dropdown.tsx
+++ b/packages/web-react/src/components/Dropdown/Dropdown.tsx
@@ -13,17 +13,19 @@ import { useDropdownStyleProps } from './useDropdownStyleProps';
const defaultProps = {
enableAutoClose: true,
- placement: Placements.BOTTOM_LEFT,
+ placement: Placements.BOTTOM_START,
};
export const Dropdown = (props: SpiritDropdownProps) => {
const {
+ /** @deprecated ID will be made a required user input in the next major version. */
id = Math.random().toString(36).slice(2, 7),
children,
- renderTrigger,
enableAutoClose,
fullWidthMode,
onAutoClose,
+ placement = defaultProps.placement,
+ renderTrigger,
...rest
} = props;
@@ -32,7 +34,7 @@ export const Dropdown = (props: SpiritDropdownProps) => {
const { isOpen, toggleHandler } = useDropdown({ dropdownRef, triggerRef, enableAutoClose, onAutoClose });
const { classProps, props: modifiedProps } = useDropdownStyleProps({ isOpen, ...rest });
- const { triggerProps, contentProps } = useDropdownAriaProps({ id, isOpen, toggleHandler, fullWidthMode });
+ const { triggerProps, contentProps } = useDropdownAriaProps({ id, isOpen, fullWidthMode, placement, toggleHandler });
const { styleProps: contentStyleProps, props: contentOtherProps } = useStyleProps({ ...modifiedProps });
const { styleProps: triggerStyleProps } = useStyleProps({
diff --git a/packages/web-react/src/components/Dropdown/DropdownContext.ts b/packages/web-react/src/components/Dropdown/DropdownContext.ts
index 10086003e8..663fa427b9 100644
--- a/packages/web-react/src/components/Dropdown/DropdownContext.ts
+++ b/packages/web-react/src/components/Dropdown/DropdownContext.ts
@@ -19,7 +19,7 @@ const defaultContext: DropdownContextType = {
id: '',
isOpen: false,
onToggle: () => {},
- placement: Placements.BOTTOM_LEFT,
+ placement: Placements.BOTTOM_START,
triggerRef: { current: undefined },
};
diff --git a/packages/web-react/src/components/Dropdown/DropdownPopover.tsx b/packages/web-react/src/components/Dropdown/DropdownPopover.tsx
index a64b3a6587..b09efd8f22 100644
--- a/packages/web-react/src/components/Dropdown/DropdownPopover.tsx
+++ b/packages/web-react/src/components/Dropdown/DropdownPopover.tsx
@@ -11,9 +11,9 @@ interface DropdownPopoverProps extends ChildrenProps, StyleProps {}
export const DropdownPopover = (props: DropdownPopoverProps) => {
const { children, ...rest } = props;
const { id, isOpen, onToggle, fullWidthMode, placement } = useDropdownContext();
- const { classProps, props: modifiedProps } = useDropdownStyleProps({ isOpen, placement, ...rest });
+ const { classProps, props: modifiedProps } = useDropdownStyleProps({ isOpen, ...rest });
const { styleProps: contentStyleProps, props: contentOtherProps } = useStyleProps({ ...modifiedProps });
- const { contentProps } = useDropdownAriaProps({ id, isOpen, toggleHandler: onToggle, fullWidthMode });
+ const { contentProps } = useDropdownAriaProps({ id, isOpen, toggleHandler: onToggle, placement, fullWidthMode });
return (
` | ✕ | Component id |
-| `onAutoClose` . | `(event: Event) => void` | — | ✕ | Callback on close on click outside of Dropdown |
-| `placement` | [Placement dictionary][dictionary-placement] | `bottom-left` | ✕ | Alignment of the component |
-| `renderTrigger` | `(render: DropdownRenderProps) => ReactNode` | — | ✕ | Properties for trigger render |
+| Name | Type | Default | Required | Description |
+| ----------------- | ------------------------------------------------ | -------------- | -------- | ---------------------------------------------- |
+| `enableAutoClose` | `bool` | `true` | ✕ | Enables close on click outside of Dropdown |
+| `fullWidthMode` | [`DropdownFullwidthMode`][dropdownfullwidthmode] | `off` | ✕ | Full-width mode |
+| `id` | `string` | `
` | ✕ | Component id |
+| `onAutoClose` . | `(event: Event) => void` | — | ✕ | Callback on close on click outside of Dropdown |
+| `placement` | [Placement dictionary][dictionary-placement] | `bottom-start` | ✕ | Alignment of the component |
+| `renderTrigger` | `(render: DropdownRenderProps) => ReactNode` | — | ✕ | Properties for trigger render |
On top of the API options, the components accept [additional attributes][readme-additional-attributes].
If you need more control over the styling of a component, you can use [style props][readme-style-props]
@@ -77,6 +77,14 @@ const onToggle = () => setIsOpen(!isOpen);
;
```
+### ⚠️ DEPRECATION NOTICE
+
+Both cross-axis placements have been renamed from `top-left`, `top-right`, `right-top`, `right-bottom`,
+etc. to `top-start`, `top-end`, `right-start`, `right-end`, etc. The old names are deprecated and will be
+removed in the next major release.
+
+[What are deprecations?][readme-deprecations]
+
### Dropdown with Item
Enhance your DropdownPopover by incorporating the versatile [Item][item] component.
@@ -111,15 +119,15 @@ import { UncontrolledDropdown, DropdownTrigger, DropdownPopover } from '@lmc-eu/
### DropdownModern
-| Name | Type | Default | Required | Description |
-| ----------------- | ------------------------------------------------ | ------------- | -------- | ---------------------------------------------- |
-| `enableAutoClose` | `bool` | `true` | ✕ | Enables close on click outside of Dropdown |
-| `fullWidthMode` | [`DropdownFullwidthMode`][dropdownfullwidthmode] | `off` | ✕ | Full-width mode |
-| `id` | `string` | - | ✔ | Component id |
-| `isOpen` | `bool` | `false` | ✔ | Open state |
-| `onAutoClose` | `(event: Event) => void` | — | ✕ | Callback on close on click outside of Dropdown |
-| `onToggle` | `() => void` | — | ✔ | Function for toggle open state of dropdown |
-| `placement` | [Placement dictionary][dictionary-placement] | `bottom-left` | ✕ | Alignment of the component |
+| Name | Type | Default | Required | Description |
+| ----------------- | ------------------------------------------------ | -------------- | -------- | ---------------------------------------------- |
+| `enableAutoClose` | `bool` | `true` | ✕ | Enables close on click outside of Dropdown |
+| `fullWidthMode` | [`DropdownFullwidthMode`][dropdownfullwidthmode] | `off` | ✕ | Full-width mode |
+| `id` | `string` | - | ✔ | Component id |
+| `isOpen` | `bool` | `false` | ✔ | Open state |
+| `onAutoClose` | `(event: Event) => void` | — | ✕ | Callback on close on click outside of Dropdown |
+| `onToggle` | `() => void` | — | ✔ | Function for toggle open state of dropdown |
+| `placement` | [Placement dictionary][dictionary-placement] | `bottom-start` | ✕ | Alignment of the component |
On top of the API options, the components accept [additional attributes][readme-additional-attributes].
If you need more control over the styling of a component, you can use [style props][readme-style-props]
@@ -148,13 +156,13 @@ and [escape hatches][readme-escape-hatches].
### UncontrolledDropdown
-| Name | Type | Default | Required | Description |
-| ----------------- | ------------------------------------------------ | ------------- | -------- | ---------------------------------------------- |
-| `enableAutoClose` | `bool` | `true` | ✕ | Enables close on click outside of Dropdown |
-| `fullWidthMode` | [`DropdownFullwidthMode`][dropdownfullwidthmode] | `off` | ✕ | Full-width mode |
-| `id` | `string` | `` | ✕ | Component id |
-| `onAutoClose` | `(event: Event) => void` | — | ✕ | Callback on close on click outside of Dropdown |
-| `placement` | [Placement dictionary][dictionary-placement] | `bottom-left` | ✕ | Alignment of the component |
+| Name | Type | Default | Required | Description |
+| ----------------- | ------------------------------------------------ | -------------- | -------- | ---------------------------------------------- |
+| `enableAutoClose` | `bool` | `true` | ✕ | Enables close on click outside of Dropdown |
+| `fullWidthMode` | [`DropdownFullwidthMode`][dropdownfullwidthmode] | `off` | ✕ | Full-width mode |
+| `id` | `string` | `` | ✕ | Component id |
+| `onAutoClose` | `(event: Event) => void` | — | ✕ | Callback on close on click outside of Dropdown |
+| `placement` | [Placement dictionary][dictionary-placement] | `bottom-start` | ✕ | Alignment of the component |
On top of the API options, the components accept [additional attributes][readme-additional-attributes].
If you need more control over the styling of a component, you can use [style props][readme-style-props]
@@ -167,5 +175,6 @@ and [escape hatches][readme-escape-hatches].
[dropdownfullwidthmode]: https://github.com/lmc-eu/spirit-design-system/blob/main/packages/web-react/src/types/dropdown.ts#L19
[item]: https://github.com/lmc-eu/spirit-design-system/blob/main/packages/web-react/src/components/Item/README.md
[readme-additional-attributes]: https://github.com/lmc-eu/spirit-design-system/blob/main/packages/web-react/README.md#additional-attributes
+[readme-deprecations]: https://github.com/lmc-eu/spirit-design-system/tree/main/packages/web-react/README.md#deprecations
[readme-escape-hatches]: https://github.com/lmc-eu/spirit-design-system/blob/main/packages/web-react/README.md#escape-hatches
[readme-style-props]: https://github.com/lmc-eu/spirit-design-system/blob/main/packages/web-react/README.md#style-props
diff --git a/packages/web-react/src/components/Dropdown/__tests__/useDropdownAriaProps.test.ts b/packages/web-react/src/components/Dropdown/__tests__/useDropdownAriaProps.test.ts
index 0579374ef7..0ed8e8870e 100644
--- a/packages/web-react/src/components/Dropdown/__tests__/useDropdownAriaProps.test.ts
+++ b/packages/web-react/src/components/Dropdown/__tests__/useDropdownAriaProps.test.ts
@@ -1,4 +1,5 @@
import { renderHook } from '@testing-library/react-hooks';
+import { PlacementDictionaryType } from '../../../types';
import { useDropdownAriaProps } from '../useDropdownAriaProps';
describe('useDropdownAriaProps', () => {
@@ -7,6 +8,7 @@ describe('useDropdownAriaProps', () => {
fullWidthMode: undefined,
id: 'test-dropdown-id',
isOpen: true,
+ placement: 'bottom-start' as PlacementDictionaryType,
toggleHandler: () => null,
};
const { result } = renderHook(() => useDropdownAriaProps(props));
@@ -15,5 +17,8 @@ describe('useDropdownAriaProps', () => {
expect(result.current.triggerProps['aria-expanded']).toBeDefined();
expect(result.current.triggerProps['aria-controls']).toBeDefined();
expect(result.current.triggerProps.onClick).toBeDefined();
+ expect(result.current.contentProps).toBeDefined();
+ expect(result.current.contentProps['data-spirit-placement']).toBeDefined();
+ expect(result.current.contentProps['data-spirit-placement']).toBe('bottom-start');
});
});
diff --git a/packages/web-react/src/components/Dropdown/__tests__/useDropdownStyleProps.test.ts b/packages/web-react/src/components/Dropdown/__tests__/useDropdownStyleProps.test.ts
index 4e906db94f..24d05a3b2b 100644
--- a/packages/web-react/src/components/Dropdown/__tests__/useDropdownStyleProps.test.ts
+++ b/packages/web-react/src/components/Dropdown/__tests__/useDropdownStyleProps.test.ts
@@ -1,12 +1,11 @@
import { renderHook } from '@testing-library/react-hooks';
-import { PlacementDictionaryType } from '../../../types';
import { useDropdownStyleProps, UseDropdownStyleProps } from '../useDropdownStyleProps';
describe('useDropdownStyleProps', () => {
it('should return defaults', () => {
const { result } = renderHook(() => useDropdownStyleProps());
- expect(result.current.classProps.contentClassName).toBe('Dropdown Dropdown--bottomLeft');
+ expect(result.current.classProps.contentClassName).toBe('Dropdown');
expect(result.current.classProps.wrapperClassName).toBe('DropdownWrapper');
});
@@ -16,19 +15,10 @@ describe('useDropdownStyleProps', () => {
} as UseDropdownStyleProps;
const { result } = renderHook(() => useDropdownStyleProps(props));
- expect(result.current.classProps.contentClassName).toBe('Dropdown Dropdown--bottomLeft is-open');
+ expect(result.current.classProps.contentClassName).toBe('Dropdown is-open');
expect(result.current.classProps.triggerClassName).toBe('is-expanded');
});
- it('should change placement', () => {
- const props = {
- placement: 'top-right' as PlacementDictionaryType,
- } as UseDropdownStyleProps;
- const { result } = renderHook(() => useDropdownStyleProps(props));
-
- expect(result.current.classProps.contentClassName).toBe('Dropdown Dropdown--topRight');
- });
-
it('should transfer additional props', () => {
const props = {
isOpen: false,
@@ -36,7 +26,7 @@ describe('useDropdownStyleProps', () => {
} as UseDropdownStyleProps;
const { result } = renderHook(() => useDropdownStyleProps(props));
- expect(result.current.classProps.contentClassName).toBe('Dropdown Dropdown--bottomLeft');
+ expect(result.current.classProps.contentClassName).toBe('Dropdown');
expect(result.current.props).toEqual({ transferProp: 'test' });
});
});
diff --git a/packages/web-react/src/components/Dropdown/demo/DropdownEnhancedShadow.tsx b/packages/web-react/src/components/Dropdown/demo/DropdownEnhancedShadow.tsx
index 52e7a87982..0530bac3a8 100644
--- a/packages/web-react/src/components/Dropdown/demo/DropdownEnhancedShadow.tsx
+++ b/packages/web-react/src/components/Dropdown/demo/DropdownEnhancedShadow.tsx
@@ -14,7 +14,7 @@ const DropdownEnhancedShadow = () => {
return (
-
+
diff --git a/packages/web-react/src/components/Dropdown/demo/DropdownFullwidthAll.tsx b/packages/web-react/src/components/Dropdown/demo/DropdownFullwidthAll.tsx
index 40e0380d16..f6fc7a806d 100644
--- a/packages/web-react/src/components/Dropdown/demo/DropdownFullwidthAll.tsx
+++ b/packages/web-react/src/components/Dropdown/demo/DropdownFullwidthAll.tsx
@@ -13,7 +13,7 @@ const DropdownFullwidthAll = () => {
);
return (
-
+
);
diff --git a/packages/web-react/src/components/Dropdown/demo/DropdownFullwidthMobileOnly.tsx b/packages/web-react/src/components/Dropdown/demo/DropdownFullwidthMobileOnly.tsx
index 9f1818422c..5dacadc91b 100644
--- a/packages/web-react/src/components/Dropdown/demo/DropdownFullwidthMobileOnly.tsx
+++ b/packages/web-react/src/components/Dropdown/demo/DropdownFullwidthMobileOnly.tsx
@@ -13,7 +13,7 @@ const DropdownFullwidthMobileOnly = () => {
);
return (
-
+
);
diff --git a/packages/web-react/src/components/Dropdown/demo/DropdownPlacements.tsx b/packages/web-react/src/components/Dropdown/demo/DropdownPlacements.tsx
index e073e0d142..046786de7e 100644
--- a/packages/web-react/src/components/Dropdown/demo/DropdownPlacements.tsx
+++ b/packages/web-react/src/components/Dropdown/demo/DropdownPlacements.tsx
@@ -7,7 +7,7 @@ import { Radio } from '../../Radio';
import { Dropdown } from '..';
const DropdownPlacements = () => {
- const [placement, setPlacement] = useState('bottom-left');
+ const [placement, setPlacement] = useState('bottom-start');
const handlePlacementChange = (e: ChangeEvent) => {
setPlacement(e.target.value as PlacementDictionaryType);
@@ -25,59 +25,59 @@ const DropdownPlacements = () => {
{' '}
{' '}
{' '}
{' '}
{
>
{
>
diff --git a/packages/web-react/src/components/Dropdown/stories/Dropdown.stories.tsx b/packages/web-react/src/components/Dropdown/stories/Dropdown.stories.tsx
index a56b3d6f15..99471a45e2 100644
--- a/packages/web-react/src/components/Dropdown/stories/Dropdown.stories.tsx
+++ b/packages/web-react/src/components/Dropdown/stories/Dropdown.stories.tsx
@@ -40,7 +40,7 @@ const meta: Meta = {
control: 'select',
options: Object.values(Placements),
table: {
- defaultValue: { summary: Placements.BOTTOM_LEFT },
+ defaultValue: { summary: Placements.BOTTOM_START },
},
},
},
@@ -66,7 +66,7 @@ const meta: Meta = {
>
),
id: 'DropdownExample',
- placement: Placements.BOTTOM_LEFT,
+ placement: Placements.BOTTOM_START,
},
};
diff --git a/packages/web-react/src/components/Dropdown/useDropdownAriaProps.ts b/packages/web-react/src/components/Dropdown/useDropdownAriaProps.ts
index 06249cec88..7919c0aced 100644
--- a/packages/web-react/src/components/Dropdown/useDropdownAriaProps.ts
+++ b/packages/web-react/src/components/Dropdown/useDropdownAriaProps.ts
@@ -1,8 +1,11 @@
-import { Booleanish, ClickEvent, DropdownFullWidthMode } from '../../types';
+import { Placements } from '../../constants';
+import { useDeprecationMessage } from '../../hooks';
+import { Booleanish, ClickEvent, DropdownFullWidthMode, PlacementDictionaryType } from '../../types';
const NAME_ARIA_EXPANDED = 'aria-expanded';
const NAME_ARIA_CONTROLS = 'aria-controls';
const NAME_DATA_FULLWIDTHMODE = 'data-spirit-fullwidthmode';
+const NAME_DATA_PLACEMENT = 'data-spirit-placement';
export enum fullWidthModeKeys {
'off' = 'off',
@@ -14,10 +17,12 @@ export interface UseDropdownAriaPropsProps {
id: string;
/** open state */
isOpen: boolean;
- /** toggle callback */
- toggleHandler: (event: ClickEvent) => void;
/** fullWidthMode */
fullWidthMode: DropdownFullWidthMode | undefined;
+ /** placement */
+ placement?: PlacementDictionaryType;
+ /** toggle callback */
+ toggleHandler: (event: ClickEvent) => void;
}
export interface UseDropdownAriaPropsReturn {
@@ -25,6 +30,7 @@ export interface UseDropdownAriaPropsReturn {
contentProps: {
id: string;
[NAME_DATA_FULLWIDTHMODE]?: keyof typeof fullWidthModeKeys | undefined;
+ [NAME_DATA_PLACEMENT]?: PlacementDictionaryType;
};
/** trigger returned props */
triggerProps: {
@@ -34,8 +40,29 @@ export interface UseDropdownAriaPropsReturn {
};
}
+const deprecatedPlacements = {
+ 'top-left': 'top-start',
+ 'top-right': 'top-end',
+ 'right-top': 'right-start',
+ 'right-bottom': 'right-end',
+ 'bottom-left': 'bottom-start',
+ 'bottom-right': 'bottom-end',
+ 'left-top': 'left-start',
+ 'left-bottom': 'left-end',
+};
+
export const useDropdownAriaProps = (props: UseDropdownAriaPropsProps): UseDropdownAriaPropsReturn => {
- const { fullWidthMode, id, isOpen, toggleHandler } = props;
+ const { fullWidthMode, id, isOpen, placement = Placements.BOTTOM_START, toggleHandler } = props;
+ useDeprecationMessage({
+ method: 'property',
+ trigger: !!deprecatedPlacements[placement as keyof typeof deprecatedPlacements],
+ componentName: 'Tooltip',
+ propertyProps: {
+ deprecatedValue: placement,
+ newValue: deprecatedPlacements[placement as keyof typeof deprecatedPlacements],
+ propertyName: 'placement',
+ },
+ });
const triggerProps = {
id,
@@ -43,7 +70,11 @@ export const useDropdownAriaProps = (props: UseDropdownAriaPropsProps): UseDropd
[NAME_ARIA_CONTROLS]: String(id),
onClick: toggleHandler,
};
- const contentProps = { id, [NAME_DATA_FULLWIDTHMODE]: fullWidthMode };
+ const contentProps = {
+ id,
+ [NAME_DATA_FULLWIDTHMODE]: fullWidthMode,
+ [NAME_DATA_PLACEMENT]: placement,
+ };
return {
contentProps,
diff --git a/packages/web-react/src/components/Dropdown/useDropdownStyleProps.ts b/packages/web-react/src/components/Dropdown/useDropdownStyleProps.ts
index 7535cfceb2..4483a874a9 100644
--- a/packages/web-react/src/components/Dropdown/useDropdownStyleProps.ts
+++ b/packages/web-react/src/components/Dropdown/useDropdownStyleProps.ts
@@ -1,8 +1,6 @@
import classNames from 'classnames';
-import { Placements } from '../../constants';
import { useClassNamePrefix } from '../../hooks';
import { DropdownProps, SpiritDropdownProps } from '../../types';
-import { kebabCaseToCamelCase } from '../../utils';
export interface UseDropdownStyleProps extends SpiritDropdownProps {
/** open state */
@@ -21,15 +19,14 @@ export interface UseDropdownStylePropsReturn {
export const useDropdownStyleProps = (
props: UseDropdownStyleProps = { isOpen: false },
): UseDropdownStylePropsReturn => {
- const { isOpen, placement = Placements.BOTTOM_LEFT, ...modifiedProps } = props;
+ const { isOpen, ...modifiedProps } = props;
const dropdownClass = useClassNamePrefix('Dropdown');
const dropdownWrapperClass = `${dropdownClass}Wrapper`;
- const dropdownPlacementClass = `${dropdownClass}--${kebabCaseToCamelCase(placement)}`;
const expandedClass = isOpen ? 'is-expanded' : '';
const openClass = isOpen ? 'is-open' : '';
- const dropdownClassName = classNames(dropdownClass, dropdownPlacementClass, openClass);
+ const dropdownClassName = classNames(dropdownClass, openClass);
const triggerClassName = classNames(expandedClass);
return {
diff --git a/packages/web-twig/src/Resources/components/Dropdown/README.md b/packages/web-twig/src/Resources/components/Dropdown/README.md
index 1f12be973e..b935c558ad 100644
--- a/packages/web-twig/src/Resources/components/Dropdown/README.md
+++ b/packages/web-twig/src/Resources/components/Dropdown/README.md
@@ -60,7 +60,7 @@ and [escape hatches][readme-escape-hatches].
#### ⚠️ DEPRECATION NOTICE
-Both cross-axis placements are renamed from `top-left`, `top-right`, `right-top`, `right-bottom`,
+Both cross-axis placements have been renamed from `top-left`, `top-right`, `right-top`, `right-bottom`,
etc. to `top-start`, `top-end`, `right-start`, `right-end`, etc. The old names are deprecated and will be
removed in the next major release.