[Product Block Editor]: Introduce ButtonWithDropdownMenuProps component (#43103)
* first approach * improve story. fix TS issues * support `variant` prop * add position props * add offset popover prop * reorganize popover props * changelog * set component roles * rotate chevron when opened * Introduce defaultOpen prop * export component * improve story * ensure 1px of gap * reorganize types * change API. Add Readme,md * dropdownButtonLabel is optional * fix border-radious * minor change in doc * pass down defaultOpen prop
This commit is contained in:
parent
de9322fb38
commit
ccdfb16fc8
|
@ -0,0 +1,4 @@
|
|||
Significance: patch
|
||||
Type: add
|
||||
|
||||
[Product Block Editor]: Introduce ButtonWithDropdownMenuProps component
|
|
@ -0,0 +1,27 @@
|
|||
# ButtonWithDropdownMenu Component
|
||||
|
||||
## Description
|
||||
|
||||
The `ButtonWithDropdownMenu` is a React component that renders a button with an associated dropdown menu. It provides flexibility in configuring the dropdown's content, appearance, and behavior.
|
||||
|
||||
## Usage
|
||||
|
||||
```jsx
|
||||
import { ButtonWithDropdownMenu } from 'path_to_component';
|
||||
|
||||
<ButtonWithDropdownMenu
|
||||
text="Add to store"
|
||||
variant="secondary"
|
||||
controls={ [
|
||||
{
|
||||
title: 'First Menu Item Label',
|
||||
onClick: () => console.log( 'First option clicked' ).
|
||||
},
|
||||
{
|
||||
title: 'Second Menu Item Label',
|
||||
onClick: () => console.log( 'Second option clicked' ).
|
||||
},
|
||||
] }
|
||||
onButtonClick={() => console.log( 'Button clicked' ) }
|
||||
/>
|
||||
```
|
|
@ -0,0 +1,71 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { createElement } from '@wordpress/element';
|
||||
import { chevronDown } from '@wordpress/icons';
|
||||
import { Button, DropdownMenu, Flex, FlexItem } from '@wordpress/components';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import type { ButtonWithDropdownMenuProps } from './types';
|
||||
|
||||
export const ButtonWithDropdownMenu: React.FC<
|
||||
ButtonWithDropdownMenuProps
|
||||
> = ( {
|
||||
text,
|
||||
dropdownButtonLabel = __( 'More options', 'woocommerce' ),
|
||||
onButtonClick = () => {},
|
||||
controls = [],
|
||||
variant = 'primary',
|
||||
defaultOpen = false,
|
||||
popoverProps: {
|
||||
placement = 'bottom-end',
|
||||
position = 'bottom left left',
|
||||
offset = 0,
|
||||
} = {
|
||||
placement: 'bottom-end',
|
||||
position: 'bottom left left',
|
||||
offset: 0,
|
||||
},
|
||||
} ) => {
|
||||
return (
|
||||
<Flex
|
||||
className="woocommerce-button-with-dropdown-menu"
|
||||
justify="left"
|
||||
gap={ 0 }
|
||||
expanded={ false }
|
||||
role="group"
|
||||
>
|
||||
<FlexItem role="none">
|
||||
<Button
|
||||
variant={ variant }
|
||||
onClick={ onButtonClick }
|
||||
className="woocommerce-button-with-dropdown-menu__main-button"
|
||||
>
|
||||
{ text }
|
||||
</Button>
|
||||
</FlexItem>
|
||||
|
||||
<FlexItem role="none">
|
||||
<DropdownMenu
|
||||
toggleProps={ {
|
||||
className:
|
||||
'woocommerce-button-with-dropdown-menu__dropdown-button',
|
||||
variant,
|
||||
} }
|
||||
controls={ controls }
|
||||
icon={ chevronDown }
|
||||
label={ dropdownButtonLabel }
|
||||
popoverProps={ {
|
||||
placement,
|
||||
// @ts-expect-error no exported member.
|
||||
position,
|
||||
offset,
|
||||
} }
|
||||
defaultOpen={ defaultOpen }
|
||||
/>
|
||||
</FlexItem>
|
||||
</Flex>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,52 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import React from 'react';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { ButtonWithDropdownMenu } from '../';
|
||||
import type { ButtonWithDropdownMenuProps } from '../types';
|
||||
|
||||
export default {
|
||||
title: 'Product Editor/components/ButtonWithDropdownMenu',
|
||||
component: ButtonWithDropdownMenu,
|
||||
};
|
||||
|
||||
export const Default = ( args: ButtonWithDropdownMenuProps ) => {
|
||||
return (
|
||||
<div
|
||||
style={ {
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
minHeight: '300px',
|
||||
} }
|
||||
>
|
||||
<ButtonWithDropdownMenu { ...args } />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Default.args = {
|
||||
text: 'Add to store',
|
||||
dropdownButtonLabel: 'More options',
|
||||
variant: 'secondary',
|
||||
defaultOpen: false,
|
||||
popoverProps: {
|
||||
placement: 'bottom-end',
|
||||
position: 'bottom left left',
|
||||
offset: 0,
|
||||
},
|
||||
controls: [
|
||||
{
|
||||
title: 'First Menu Item Label',
|
||||
onClick: function noRefCheck() {},
|
||||
},
|
||||
{
|
||||
onClick: function noRefCheck() {},
|
||||
title: 'Second Menu Item Label',
|
||||
},
|
||||
],
|
||||
onButtonClick: console.log, // eslint-disable-line no-console
|
||||
};
|
|
@ -0,0 +1,19 @@
|
|||
.woocommerce-button-with-dropdown-menu {
|
||||
&__main-button {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
&__dropdown-button {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
|
||||
&.is-opened svg {
|
||||
transform: rotate( 180deg ); // @todo: consider to set the is-opened in the local state
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-button-with-dropdown-menu.components-flex {
|
||||
gap: 1px;
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { Button } from '@wordpress/components';
|
||||
import type {
|
||||
// @ts-expect-error no exported member.
|
||||
DropdownOption,
|
||||
} from '@wordpress/components';
|
||||
|
||||
type ButtonVariant = Button.ButtonProps[ 'variant' ];
|
||||
|
||||
type PositionYAxis = 'top' | 'middle' | 'bottom';
|
||||
type PositionXAxis = 'left' | 'center' | 'right';
|
||||
type PositionCorner = 'top' | 'right' | 'bottom' | 'left';
|
||||
|
||||
type PopoverPlacement =
|
||||
| 'left'
|
||||
| 'right'
|
||||
| 'bottom'
|
||||
| 'top'
|
||||
| 'left-end'
|
||||
| 'left-start'
|
||||
| 'right-end'
|
||||
| 'right-start'
|
||||
| 'bottom-end'
|
||||
| 'bottom-start'
|
||||
| 'top-end'
|
||||
| 'top-start'; // @todo: pick from core
|
||||
|
||||
type popoverPosition =
|
||||
| `${ PositionYAxis }`
|
||||
| `${ PositionYAxis } ${ PositionXAxis }`
|
||||
| `${ PositionYAxis } ${ PositionXAxis } ${ PositionCorner }`;
|
||||
|
||||
type popoverProps = {
|
||||
placement?: PopoverPlacement;
|
||||
position?: popoverPosition;
|
||||
offset?: number;
|
||||
};
|
||||
|
||||
export interface ButtonWithDropdownMenuProps {
|
||||
text: string;
|
||||
dropdownButtonLabel?: string;
|
||||
variant?: ButtonVariant;
|
||||
defaultOpen?: boolean;
|
||||
controls?: DropdownOption[];
|
||||
|
||||
popoverProps?: popoverProps;
|
||||
onButtonClick?: () => void;
|
||||
}
|
|
@ -65,3 +65,4 @@ export {
|
|||
} from './block-slot-fill';
|
||||
|
||||
export { Label as __experimentalLabel } from './label/label';
|
||||
export { ButtonWithDropdownMenu as __experimentalButtonWithDropdownMenu } from './button-with-dropdown-menu';
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
@import "components/checkbox-control/style.scss";
|
||||
@import "components/add-products-modal/style.scss";
|
||||
@import "components/advice-card/style.scss";
|
||||
@import "components/button-with-dropdown-menu/style.scss";
|
||||
|
||||
/* Field Blocks */
|
||||
|
||||
|
|
Loading…
Reference in New Issue