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

InputControl: Add padding wrapper for prefix/suffix #42378

Merged
merged 10 commits into from
Jul 20, 2022
Merged
Show file tree
Hide file tree
Changes from all 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 @@ -10,6 +10,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)).

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