Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dropdown: Add wrapper for custom padding #42595

Merged
merged 5 commits into from
Jul 22, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/components/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
- `UnitControl`: Update unit select's focus styles to match input's ([#42383](https://github.com/WordPress/gutenberg/pull/42383)).
- `CustomSelectControl`: Add size variants ([#42460](https://github.com/WordPress/gutenberg/pull/42460/)).
- `CustomSelectControl`: Add flag to opt in to unconstrained width ([#42460](https://github.com/WordPress/gutenberg/pull/42460/)).
- `Dropdown`: Implement wrapper subcomponent for adding different padding to the dropdown content ([#42595](https://github.com/WordPress/gutenberg/pull/42595/)).

### Internal

Expand Down
52 changes: 52 additions & 0 deletions packages/components/src/dropdown/dropdown-content-wrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* External dependencies
*/
import type { ForwardedRef } from 'react';

/**
* Internal dependencies
*/
import {
WordPressComponentProps,
contextConnect,
useContextSystem,
} from '../ui/context';
import { DropdownContentWrapperDiv } from './styles';
import type { DropdownContentWrapperProps } from './types';

function UnconnectedDropdownContentWrapper(
props: WordPressComponentProps< DropdownContentWrapperProps, 'div', false >,
forwardedRef: ForwardedRef< any >
) {
const derivedProps = useContextSystem( props, 'DropdownContentWrapper' );

return (
<DropdownContentWrapperDiv { ...derivedProps } ref={ forwardedRef } />
);
}

/**
* A convenience wrapper for the `renderContent` when you want to apply
* different padding. (Default is `paddingSize="small"`).
*
* ```jsx
* import {
* Dropdown,
* __experimentalDropdownContentWrapper as DropdownContentWrapper,
* } from '@wordpress/components';
*
* <Dropdown
* renderContent={ () => (
* <DropdownContentWrapper paddingSize="medium">
* My dropdown content
* </DropdownContentWrapper>
* ) }
* />
* ```
*/
export const DropdownContentWrapper = contextConnect(
UnconnectedDropdownContentWrapper,
'DropdownContentWrapper'
);

export default DropdownContentWrapper;
129 changes: 60 additions & 69 deletions packages/components/src/dropdown/stories/index.js
Original file line number Diff line number Diff line change
@@ -1,81 +1,72 @@
/**
* WordPress dependencies
*/
import {
more,
arrowLeft,
arrowRight,
arrowUp,
arrowDown,
} from '@wordpress/icons';
Comment on lines -4 to -10
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had to do some basic cleanup of the story first. It may be easier to review the commits separately.


/**
* Internal dependencies
*/
import Dropdown from '../';
import Button from '../../button';
import MenuGroup from '../../menu-group';
import MenuItem from '../../menu-item';
import DropdownMenu from '../../dropdown-menu';
import { DropdownContentWrapper } from '../dropdown-content-wrapper';

export default { title: 'Components/Dropdown', component: Dropdown };
export default {
title: 'Components/Dropdown',
component: Dropdown,
subcomponents: { DropdownContentWrapper },
argTypes: {
expandOnMobile: { control: { type: 'boolean' } },
focusOnMount: {
control: {
type: 'radio',
options: [ 'firstElement', 'container', false ],
},
},
headerTitle: { control: { type: 'text' } },
renderContent: { control: { type: null } },
renderToggle: { control: { type: null } },
},
};

const DropdownAndDropdownMenuExample = () => {
const Template = ( args ) => {
return (
<>
<div>
<p>This is a DropdownMenu component:</p>
<DropdownMenu
mirka marked this conversation as resolved.
Show resolved Hide resolved
icon={ more }
label="Select a direction"
controls={ [
{
title: 'Up',
icon: arrowUp,
},
{
title: 'Right',
icon: arrowRight,
},
{
title: 'Down',
icon: arrowDown,
},
{
title: 'Left',
icon: arrowLeft,
},
] }
/>
</div>
<div>
<p>This is an assembled Dropdown component:</p>
<Dropdown
className="my-container-class-name"
contentClassName="my-popover-content-classname"
position="bottom right"
renderToggle={ ( { isOpen, onToggle } ) => (
<Button
icon={ more }
onClick={ onToggle }
aria-expanded={ isOpen }
label="Select a direction"
/>
) }
renderContent={ () => (
<MenuGroup>
<MenuItem icon={ arrowUp }>Up</MenuItem>
<MenuItem icon={ arrowDown }>Down</MenuItem>
<MenuItem icon={ arrowLeft }>Left</MenuItem>
<MenuItem icon={ arrowRight }>Right</MenuItem>
</MenuGroup>
) }
/>
</div>
</>
<div style={ { height: 150 } }>
<Dropdown { ...args } />
</div>
);
};

export const _default = () => {
return <DropdownAndDropdownMenuExample />;
export const Default = Template.bind( {} );
Default.args = {
position: 'bottom right',
renderToggle: ( { isOpen, onToggle } ) => (
<Button onClick={ onToggle } aria-expanded={ isOpen } isPrimary>
Open dropdown
</Button>
),
renderContent: () => <div>This is the dropdown content.</div>,
};

/**
* To apply more padding to the dropdown content, use the provided `<DropdownContentWrapper>`
* convenience wrapper. A `paddingSize` of `"medium"` is suitable for relatively larger dropdowns (default is `"small"`).
*/
export const WithMorePadding = Template.bind( {} );
WithMorePadding.args = {
...Default.args,
renderContent: () => (
<DropdownContentWrapper paddingSize="medium">
Content wrapped with <code>{ `paddingSize="medium"` }</code>.
</DropdownContentWrapper>
),
};

/**
* The `<DropdownContentWrapper>` convenience wrapper can also be used to remove padding entirely,
* with a `paddingSize` of `"none"`. This can also serve as a clean foundation to add arbitrary
* paddings, for example when child components already have padding on their own.
*/
export const WithNoPadding = Template.bind( {} );
WithNoPadding.args = {
...Default.args,
renderContent: () => (
<DropdownContentWrapper paddingSize="none">
Content wrapped with <code>{ `paddingSize="none"` }</code>.
</DropdownContentWrapper>
),
};
31 changes: 31 additions & 0 deletions packages/components/src/dropdown/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* External dependencies
*/
import { css } from '@emotion/react';
import styled from '@emotion/styled';

/**
* Internal dependencies
*/
import { space } from '../ui/utils/space';
import type { DropdownContentWrapperProps } from './types';

const padding = ( { paddingSize = 'small' }: DropdownContentWrapperProps ) => {
mirka marked this conversation as resolved.
Show resolved Hide resolved
if ( paddingSize === 'none' ) return;

const paddingValues = {
small: space( 2 ),
medium: space( 4 ),
};

return css`
padding: ${ paddingValues[ paddingSize ] || paddingValues.small };
`;
};

export const DropdownContentWrapperDiv = styled.div< DropdownContentWrapperProps >`
// Negative margin to reset (offset) the default padding on .components-popover__content
margin: ${ space( -2 ) };

${ padding };
`;
8 changes: 8 additions & 0 deletions packages/components/src/dropdown/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export type DropdownContentWrapperProps = {
/**
* Amount of padding to apply on the dropdown content.
*
* @default 'small'
*/
paddingSize?: 'none' | 'small' | 'medium';
};
1 change: 1 addition & 0 deletions packages/components/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export { default as Draggable } from './draggable';
export { default as DropZone } from './drop-zone';
export { default as DropZoneProvider } from './drop-zone/provider';
export { default as Dropdown } from './dropdown';
export { default as __experimentalDropdownContentWrapper } from './dropdown/dropdown-content-wrapper';
export { default as DropdownMenu } from './dropdown-menu';
export { DuotoneSwatch, DuotonePicker } from './duotone-picker';
export { Elevation as __experimentalElevation } from './elevation';
Expand Down