Skip to content

Commit

Permalink
InputControl: Add padding wrapper for prefix/suffix (#42378)
Browse files Browse the repository at this point in the history
* InputControl: Add padding wrapper for prefix/suffix

* Add changelog

* Update snapshot

* Memoize and simplify context value

* Remove padding props

* Rename wrapper component

* Rename files

* Fixup changelog

* Fixup
  • Loading branch information
mirka authored Jul 20, 2022
1 parent 4bb127e commit b5fce3f
Show file tree
Hide file tree
Showing 10 changed files with 202 additions and 26 deletions.
1 change: 1 addition & 0 deletions packages/components/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

- `BorderControl`: Improve labelling, tooltips and DOM structure ([#42348](https://github.com/WordPress/gutenberg/pull/42348/)).
- `BaseControl`: Set zero padding on `StyledLabel` to ensure cross-browser styling ([#42348](https://github.com/WordPress/gutenberg/pull/42348/)).
- `InputControl`: Implement wrapper subcomponents for adding responsive padding to `prefix`/`suffix` ([#42378](https://github.com/WordPress/gutenberg/pull/42378)).
- `SelectControl`: Add flag for larger default size ([#42456](https://github.com/WordPress/gutenberg/pull/42456/)).
- `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/)).
Expand Down
2 changes: 2 additions & 0 deletions packages/components/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ export {
Item as __experimentalItem,
} from './item-group';
export { default as __experimentalInputControl } from './input-control';
export { default as __experimentalInputControlPrefixWrapper } from './input-control/input-prefix-wrapper';
export { default as __experimentalInputControlSuffixWrapper } from './input-control/input-suffix-wrapper';
export { default as KeyboardShortcuts } from './keyboard-shortcuts';
export { default as MenuGroup } from './menu-group';
export { default as MenuItem } from './menu-item';
Expand Down
3 changes: 3 additions & 0 deletions packages/components/src/input-control/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ function useUniqueId( idProp?: string ) {

export function UnforwardedInputControl(
{
__next36pxDefaultSize,
__unstableStateReducer: stateReducer = ( state ) => state,
__unstableInputWidth,
className,
Expand Down Expand Up @@ -63,6 +64,7 @@ export function UnforwardedInputControl(

return (
<InputBase
__next36pxDefaultSize={ __next36pxDefaultSize }
__unstableInputWidth={ __unstableInputWidth }
className={ classes }
disabled={ disabled }
Expand All @@ -79,6 +81,7 @@ export function UnforwardedInputControl(
>
<InputField
{ ...props }
__next36pxDefaultSize={ __next36pxDefaultSize }
className="components-input-control__input"
disabled={ disabled }
id={ id }
Expand Down
40 changes: 28 additions & 12 deletions packages/components/src/input-control/input-base.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type { ForwardedRef } from 'react';
* WordPress dependencies
*/
import { useInstanceId } from '@wordpress/compose';
import { forwardRef } from '@wordpress/element';
import { forwardRef, useMemo } from '@wordpress/element';

/**
* Internal dependencies
Expand All @@ -20,8 +20,10 @@ import {
Prefix,
Suffix,
LabelWrapper,
getSizeConfig,
} from './styles/input-control-styles';
import type { InputBaseProps, LabelPosition } from './types';
import { ContextSystemProvider } from '../ui/context';

function useUniqueId( idProp?: string ) {
const instanceId = useInstanceId( InputBase );
Expand Down Expand Up @@ -52,6 +54,7 @@ function getUIFlexProps( labelPosition?: LabelPosition ) {

export function InputBase(
{
__next36pxDefaultSize,
__unstableInputWidth,
children,
className,
Expand All @@ -71,6 +74,17 @@ export function InputBase(
const id = useUniqueId( idProp );
const hideLabel = hideLabelFromVision || ! label;

const { paddingLeft, paddingRight } = getSizeConfig( {
inputSize: size,
__next36pxDefaultSize,
} );
const prefixSuffixContextValue = useMemo( () => {
return {
InputControlPrefixWrapper: { paddingLeft },
InputControlSuffixWrapper: { paddingRight },
};
}, [ paddingLeft, paddingRight ] );

return (
// @ts-expect-error The `direction` prop from Flex (FlexDirection) conflicts with legacy SVGAttributes `direction` (string) that come from React intrinsic prop definitions.
<Root
Expand Down Expand Up @@ -99,17 +113,19 @@ export function InputBase(
hideLabel={ hideLabel }
labelPosition={ labelPosition }
>
{ prefix && (
<Prefix className="components-input-control__prefix">
{ prefix }
</Prefix>
) }
{ children }
{ suffix && (
<Suffix className="components-input-control__suffix">
{ suffix }
</Suffix>
) }
<ContextSystemProvider value={ prefixSuffixContextValue }>
{ prefix && (
<Prefix className="components-input-control__prefix">
{ prefix }
</Prefix>
) }
{ children }
{ suffix && (
<Suffix className="components-input-control__suffix">
{ suffix }
</Suffix>
) }
</ContextSystemProvider>
<Backdrop disabled={ disabled } isFocused={ isFocused } />
</Container>
</Root>
Expand Down
48 changes: 48 additions & 0 deletions packages/components/src/input-control/input-prefix-wrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* External dependencies
*/
import type { ForwardedRef } from 'react';

/**
* Internal dependencies
*/
import { Spacer } from '../spacer';
import {
WordPressComponentProps,
contextConnect,
useContextSystem,
} from '../ui/context';
import type { InputControlPrefixWrapperProps } from './types';

function UnconnectedInputControlPrefixWrapper(
props: WordPressComponentProps< InputControlPrefixWrapperProps, 'div' >,
forwardedRef: ForwardedRef< any >
) {
const derivedProps = useContextSystem( props, 'InputControlPrefixWrapper' );

return (
<Spacer marginBottom={ 0 } { ...derivedProps } ref={ forwardedRef } />
);
}

/**
* A convenience wrapper for the `prefix` when you want to apply
* standard padding in accordance with the size variant.
*
* ```jsx
* import {
* __experimentalInputControl as InputControl,
* __experimentalInputControlPrefixWrapper as InputControlPrefixWrapper,
* } from '@wordpress/components';
*
* <InputControl
* prefix={<InputControlPrefixWrapper>@</InputControlPrefixWrapper>}
* />
* ```
*/
export const InputControlPrefixWrapper = contextConnect(
UnconnectedInputControlPrefixWrapper,
'InputControlPrefixWrapper'
);

export default InputControlPrefixWrapper;
48 changes: 48 additions & 0 deletions packages/components/src/input-control/input-suffix-wrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* External dependencies
*/
import type { ForwardedRef } from 'react';

/**
* Internal dependencies
*/
import { Spacer } from '../spacer';
import {
WordPressComponentProps,
contextConnect,
useContextSystem,
} from '../ui/context';
import type { InputControlSuffixWrapperProps } from './types';

function UnconnectedInputControlSuffixWrapper(
props: WordPressComponentProps< InputControlSuffixWrapperProps, 'div' >,
forwardedRef: ForwardedRef< any >
) {
const derivedProps = useContextSystem( props, 'InputControlSuffixWrapper' );

return (
<Spacer marginBottom={ 0 } { ...derivedProps } ref={ forwardedRef } />
);
}

/**
* A convenience wrapper for the `suffix` when you want to apply
* standard padding in accordance with the size variant.
*
* ```jsx
* import {
* __experimentalInputControl as InputControl,
* __experimentalInputControlSuffixWrapper as InputControlSuffixWrapper,
* } from '@wordpress/components';
*
* <InputControl
* suffix={<InputControlSuffixWrapper>%</InputControlSuffixWrapper>}
* />
* ```
*/
export const InputControlSuffixWrapper = contextConnect(
UnconnectedInputControlSuffixWrapper,
'InputControlSuffixWrapper'
);

export default InputControlSuffixWrapper;
17 changes: 15 additions & 2 deletions packages/components/src/input-control/stories/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@ import type { ComponentMeta, ComponentStory } from '@storybook/react';
* Internal dependencies
*/
import InputControl from '..';
import { InputControlPrefixWrapper } from '../input-prefix-wrapper';
import { InputControlSuffixWrapper } from '../input-suffix-wrapper';

const meta: ComponentMeta< typeof InputControl > = {
title: 'Components (Experimental)/InputControl',
component: InputControl,
subcomponents: { InputControlPrefixWrapper, InputControlSuffixWrapper },
argTypes: {
__unstableInputWidth: { control: { type: 'text' } },
__unstableStateReducer: { control: { type: null } },
Expand All @@ -37,16 +40,26 @@ Default.args = {
placeholder: 'Placeholder',
};

/**
* A `prefix` can be inserted before the input. By default, the prefix is aligned with the edge of the input border,
* with no padding. If you want to apply standard padding in accordance with the size variant, use the provided
* `<InputControlPrefixWrapper>` convenience wrapper.
*/
export const WithPrefix = Template.bind( {} );
WithPrefix.args = {
...Default.args,
prefix: <span style={ { marginInlineStart: 8 } }>@</span>,
prefix: <InputControlPrefixWrapper>@</InputControlPrefixWrapper>,
};

/**
* A `suffix` can be inserted after the input. By default, the suffix is aligned with the edge of the input border,
* with no padding. If you want to apply standard padding in accordance with the size variant, use the provided
* `<InputControlSuffixWrapper>` convenience wrapper.
*/
export const WithSuffix = Template.bind( {} );
WithSuffix.args = {
...Default.args,
suffix: <button style={ { marginInlineEnd: 4 } }>Send</button>,
suffix: <InputControlSuffixWrapper>%</InputControlSuffixWrapper>,
};

export const WithSideLabel = Template.bind( {} );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { Flex, FlexItem } from '../../flex';
import { Text } from '../../text';
import { COLORS, rtl } from '../../utils';
import type { LabelPosition, Size } from '../types';
import { space } from '../../ui/utils/space';

type ContainerProps = {
disabled?: boolean;
Expand Down Expand Up @@ -143,7 +144,7 @@ const fontSizeStyles = ( { inputSize: size }: InputProps ) => {
`;
};

const sizeStyles = ( {
export const getSizeConfig = ( {
inputSize: size,
__next36pxDefaultSize,
}: InputProps ) => {
Expand All @@ -153,22 +154,22 @@ const sizeStyles = ( {
height: 36,
lineHeight: 1,
minHeight: 36,
paddingLeft: 16,
paddingRight: 16,
paddingLeft: space( 4 ),
paddingRight: space( 4 ),
},
small: {
height: 24,
lineHeight: 1,
minHeight: 24,
paddingLeft: 8,
paddingRight: 8,
paddingLeft: space( 2 ),
paddingRight: space( 2 ),
},
'__unstable-large': {
height: 40,
lineHeight: 1,
minHeight: 40,
paddingLeft: 16,
paddingRight: 16,
paddingLeft: space( 4 ),
paddingRight: space( 4 ),
},
};

Expand All @@ -177,14 +178,16 @@ const sizeStyles = ( {
height: 30,
lineHeight: 1,
minHeight: 30,
paddingLeft: 8,
paddingRight: 8,
paddingLeft: space( 2 ),
paddingRight: space( 2 ),
};
}

const style = sizes[ size as Size ] || sizes.default;
return sizes[ size as Size ] || sizes.default;
};

return css( style );
const sizeStyles = ( props: InputProps ) => {
return css( getSizeConfig( props ) );
};

const customPaddings = ( {
Expand Down
42 changes: 42 additions & 0 deletions packages/components/src/input-control/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,38 @@ export interface InputBaseProps extends BaseProps, FlexProps {
children: ReactNode;
/**
* Renders an element on the left side of the input.
*
* By default, the prefix is aligned with the edge of the input border, with no padding.
* If you want to apply standard padding in accordance with the size variant, wrap the element in
* the provided `<InputControlPrefixWrapper>` component.
*
* @example
* import {
* __experimentalInputControl as InputControl,
* __experimentalInputControlPrefixWrapper as InputControlPrefixWrapper,
* } from '@wordpress/components';
*
* <InputControl
* prefix={<InputControlPrefixWrapper>@</InputControlPrefixWrapper>}
* />
*/
prefix?: ReactNode;
/**
* Renders an element on the right side of the input.
*
* By default, the suffix is aligned with the edge of the input border, with no padding.
* If you want to apply standard padding in accordance with the size variant, wrap the element in
* the provided `<InputControlSuffixWrapper>` component.
*
* @example
* import {
* __experimentalInputControl as InputControl,
* __experimentalInputControlSuffixWrapper as InputControlSuffixWrapper,
* } from '@wordpress/components';
*
* <InputControl
* suffix={<InputControlSuffixWrapper>%</InputControlSuffixWrapper>}
* />
*/
suffix?: ReactNode;
/**
Expand Down Expand Up @@ -175,3 +203,17 @@ export interface InputControlLabelProps {
labelPosition?: BaseProps[ 'labelPosition' ];
size?: BaseProps[ 'size' ];
}

export type InputControlPrefixWrapperProps = {
/**
* The prefix to be inserted.
*/
children: ReactNode;
};

export type InputControlSuffixWrapperProps = {
/**
* The suffix to be inserted.
*/
children: ReactNode;
};
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Snapshot Diff:
>
<input
autocomplete="off"
class="components-input-control__input css-1vhigq9-Input-dragStyles-fontSizeStyles-sizeStyles-customPaddings em5sgkm5"
class="components-input-control__input css-1hsyo1w-Input-dragStyles-fontSizeStyles-sizeStyles-customPaddings em5sgkm5"
- id="inspector-input-control-1"
+ id="inspector-input-control-2"
inputmode="numeric"
Expand Down

0 comments on commit b5fce3f

Please sign in to comment.