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

Add variants to InputControl prefix/suffix wrappers #64824

Merged
merged 4 commits into from
Aug 27, 2024
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 @@ -20,6 +20,7 @@
- `TabPanel`: Remove radius applied to panel focus style ([#64693](https://github.com/WordPress/gutenberg/pull/64693)).
- `Tabs`: Remove radius applied to panel focus style ([#64693](https://github.com/WordPress/gutenberg/pull/64693)).
- `UnitControl`: Update unit select styles ([#64712](https://github.com/WordPress/gutenberg/pull/64712)).
- `InputControl`: Add variants to prefix/suffix wrappers ([#64824](https://github.com/WordPress/gutenberg/pull/64824)).
- `Navigator`: remove location history, simplify internal logic ([#64675](https://github.com/WordPress/gutenberg/pull/64675)).
- Decrease horizontal padding from 16px to 12px on the following components, when in the 40px default size ([#64708](https://github.com/WordPress/gutenberg/pull/64708)).
- `AnglePickerControl`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ exports[`DimensionControl rendering renders with custom sizes 1`] = `
min-height: 0;
}

.emotion-6:focus-within:not( :has( :is( .em5sgkm7, .emotion-19 ):focus-within ) ) .emotion-26 {
.emotion-6:focus-within:not( :has( :is( .em5sgkm8, .emotion-19 ):focus-within ) ) .emotion-26 {
border-color: var(--wp-components-color-accent, var(--wp-admin-theme-color, #3858e9));
box-shadow: 0 0 0 0.5px var(--wp-components-color-accent, var(--wp-admin-theme-color, #3858e9));
outline: 2px solid transparent;
Expand Down Expand Up @@ -157,8 +157,8 @@ exports[`DimensionControl rendering renders with custom sizes 1`] = `
}

.emotion-21 {
margin-bottom: 0;
padding-right: 8px;
-webkit-padding-end: 8px;
padding-inline-end: 8px;
position: absolute;
pointer-events: none;
right: 0;
Expand Down Expand Up @@ -249,7 +249,7 @@ exports[`DimensionControl rendering renders with custom sizes 1`] = `
class="components-input-control__suffix emotion-18 emotion-19"
>
<div
class="components-spacer components-input-control-suffix-wrapper emotion-20 emotion-21 emotion-7"
class="components-input-control-suffix-wrapper emotion-20 emotion-21 emotion-22"
data-wp-c16t="true"
data-wp-component="InputControlSuffixWrapper"
>
Expand Down Expand Up @@ -327,7 +327,7 @@ exports[`DimensionControl rendering renders with defaults 1`] = `
min-height: 0;
}

.emotion-6:focus-within:not( :has( :is( .em5sgkm7, .emotion-19 ):focus-within ) ) .emotion-26 {
.emotion-6:focus-within:not( :has( :is( .em5sgkm8, .emotion-19 ):focus-within ) ) .emotion-26 {
border-color: var(--wp-components-color-accent, var(--wp-admin-theme-color, #3858e9));
box-shadow: 0 0 0 0.5px var(--wp-components-color-accent, var(--wp-admin-theme-color, #3858e9));
outline: 2px solid transparent;
Expand Down Expand Up @@ -439,8 +439,8 @@ exports[`DimensionControl rendering renders with defaults 1`] = `
}

.emotion-21 {
margin-bottom: 0;
padding-right: 8px;
-webkit-padding-end: 8px;
padding-inline-end: 8px;
position: absolute;
pointer-events: none;
right: 0;
Expand Down Expand Up @@ -541,7 +541,7 @@ exports[`DimensionControl rendering renders with defaults 1`] = `
class="components-input-control__suffix emotion-18 emotion-19"
>
<div
class="components-spacer components-input-control-suffix-wrapper emotion-20 emotion-21 emotion-7"
class="components-input-control-suffix-wrapper emotion-20 emotion-21 emotion-22"
data-wp-c16t="true"
data-wp-component="InputControlSuffixWrapper"
>
Expand Down Expand Up @@ -619,7 +619,7 @@ exports[`DimensionControl rendering renders with icon and custom icon label 1`]
min-height: 0;
}

.emotion-6:focus-within:not( :has( :is( .em5sgkm7, .emotion-19 ):focus-within ) ) .emotion-26 {
.emotion-6:focus-within:not( :has( :is( .em5sgkm8, .emotion-19 ):focus-within ) ) .emotion-26 {
border-color: var(--wp-components-color-accent, var(--wp-admin-theme-color, #3858e9));
box-shadow: 0 0 0 0.5px var(--wp-components-color-accent, var(--wp-admin-theme-color, #3858e9));
outline: 2px solid transparent;
Expand Down Expand Up @@ -731,8 +731,8 @@ exports[`DimensionControl rendering renders with icon and custom icon label 1`]
}

.emotion-21 {
margin-bottom: 0;
padding-right: 8px;
-webkit-padding-end: 8px;
padding-inline-end: 8px;
position: absolute;
pointer-events: none;
right: 0;
Expand Down Expand Up @@ -845,7 +845,7 @@ exports[`DimensionControl rendering renders with icon and custom icon label 1`]
class="components-input-control__suffix emotion-18 emotion-19"
>
<div
class="components-spacer components-input-control-suffix-wrapper emotion-20 emotion-21 emotion-7"
class="components-input-control-suffix-wrapper emotion-20 emotion-21 emotion-22"
data-wp-c16t="true"
data-wp-component="InputControlSuffixWrapper"
>
Expand Down Expand Up @@ -923,7 +923,7 @@ exports[`DimensionControl rendering renders with icon and default icon label 1`]
min-height: 0;
}

.emotion-6:focus-within:not( :has( :is( .em5sgkm7, .emotion-19 ):focus-within ) ) .emotion-26 {
.emotion-6:focus-within:not( :has( :is( .em5sgkm8, .emotion-19 ):focus-within ) ) .emotion-26 {
border-color: var(--wp-components-color-accent, var(--wp-admin-theme-color, #3858e9));
box-shadow: 0 0 0 0.5px var(--wp-components-color-accent, var(--wp-admin-theme-color, #3858e9));
outline: 2px solid transparent;
Expand Down Expand Up @@ -1035,8 +1035,8 @@ exports[`DimensionControl rendering renders with icon and default icon label 1`]
}

.emotion-21 {
margin-bottom: 0;
padding-right: 8px;
-webkit-padding-end: 8px;
padding-inline-end: 8px;
position: absolute;
pointer-events: none;
right: 0;
Expand Down Expand Up @@ -1149,7 +1149,7 @@ exports[`DimensionControl rendering renders with icon and default icon label 1`]
class="components-input-control__suffix emotion-18 emotion-19"
>
<div
class="components-spacer components-input-control-suffix-wrapper emotion-20 emotion-21 emotion-7"
class="components-input-control-suffix-wrapper emotion-20 emotion-21 emotion-22"
data-wp-c16t="true"
data-wp-component="InputControlSuffixWrapper"
>
Expand Down
18 changes: 4 additions & 14 deletions packages/components/src/input-control/input-base.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,7 @@ import { useMemo } from '@wordpress/element';
*/
import Backdrop from './backdrop';
import Label from './label';
import {
Container,
Root,
Prefix,
Suffix,
getSizeConfig,
} from './styles/input-control-styles';
import { Container, Root, Prefix, Suffix } from './styles/input-control-styles';
import type { InputBaseProps, LabelPosition } from './types';
import type { WordPressComponentProps } from '../context';
import {
Expand Down Expand Up @@ -90,16 +84,12 @@ function InputBase(
const id = useUniqueId( idProp );
const hideLabel = hideLabelFromVision || ! label;

const { paddingLeft, paddingRight } = getSizeConfig( {
inputSize: size,
__next40pxDefaultSize,
} );
const prefixSuffixContextValue = useMemo( () => {
return {
InputControlPrefixWrapper: { paddingLeft: `${ paddingLeft }px` },
InputControlSuffixWrapper: { paddingRight: `${ paddingRight }px` },
InputControlPrefixWrapper: { __next40pxDefaultSize, size },
InputControlSuffixWrapper: { __next40pxDefaultSize, size },
};
}, [ paddingLeft, paddingRight ] );
Comment on lines -93 to -102
Copy link
Member Author

Choose a reason for hiding this comment

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

Moving all the style logic into the styles file.

}, [ __next40pxDefaultSize, size ] );

return (
// @ts-expect-error The `direction` prop from Flex (FlexDirection) conflicts with legacy SVGAttributes `direction` (string) that come from React intrinsic prop definitions.
Expand Down
12 changes: 8 additions & 4 deletions packages/components/src/input-control/input-prefix-wrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,23 @@ import type { ForwardedRef } from 'react';
/**
* Internal dependencies
*/
import { Spacer } from '../spacer';
import type { WordPressComponentProps } from '../context';
import { contextConnect, useContextSystem } from '../context';
import type { InputControlPrefixWrapperProps } from './types';
import type { PrefixSuffixWrapperProps } from './types';
import { PrefixSuffixWrapper } from './styles/input-control-styles';

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

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

Expand Down
10 changes: 4 additions & 6 deletions packages/components/src/input-control/input-suffix-wrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,18 @@ import type { ForwardedRef } from 'react';
/**
* Internal dependencies
*/
import { Spacer } from '../spacer';
import type { WordPressComponentProps } from '../context';
import { contextConnect, useContextSystem } from '../context';
import type { InputControlSuffixWrapperProps } from './types';
import type { PrefixSuffixWrapperProps } from './types';
import { PrefixSuffixWrapper } from './styles/input-control-styles';

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

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

/**
Expand Down
43 changes: 31 additions & 12 deletions packages/components/src/input-control/stories/index.story.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { Meta, StoryFn } from '@storybook/react';
/**
* WordPress dependencies
*/
import { seen, unseen } from '@wordpress/icons';
import { closeSmall, Icon, link, seen, unseen } from '@wordpress/icons';
import { useState } from '@wordpress/element';
/**
* Internal dependencies
Expand Down Expand Up @@ -75,6 +75,29 @@ WithSuffix.args = {
suffix: <InputControlSuffixWrapper>%</InputControlSuffixWrapper>,
};

/**
* `<InputControlPrefixWrapper>` and `<InputControlSuffixWrapper>` have a `variant` prop that can be used to
* adjust the wrapper based on the prefix or suffix content.
*
* - `'default'`: Standard padding for text content.
* - `'icon'`: For icons.
* - `'control'`: For controls, like buttons or selects.
*/
export const WithIconOrControl = Template.bind( {} );
WithIconOrControl.args = {
...Default.args,
prefix: (
<InputControlPrefixWrapper variant="icon">
<Icon icon={ link } />
</InputControlPrefixWrapper>
),
suffix: (
<InputControlSuffixWrapper variant="control">
<Button icon={ closeSmall } size="small" label="Clear" />
</InputControlSuffixWrapper>
),
};

export const WithSideLabel = Template.bind( {} );
WithSideLabel.args = {
...Default.args,
Expand All @@ -95,17 +118,13 @@ export const ShowPassword: StoryFn< typeof InputControl > = ( args ) => {
type={ visible ? 'text' : 'password' }
label="Password"
suffix={
<InputControlSuffixWrapper>
<div style={ { display: 'flex' } }>
<Button
size="small"
icon={ visible ? unseen : seen }
onClick={ () => setVisible( ( value ) => ! value ) }
label={
visible ? 'Hide password' : 'Show password'
}
/>
</div>
<InputControlSuffixWrapper variant="control">
<Button
size="small"
icon={ visible ? unseen : seen }
onClick={ () => setVisible( ( value ) => ! value ) }
label={ visible ? 'Hide password' : 'Show password' }
/>
</InputControlSuffixWrapper>
}
{ ...args }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import type { WordPressComponentProps } from '../../context';
import { Flex, FlexItem } from '../../flex';
import { Text } from '../../text';
import { baseLabelTypography, COLORS, CONFIG, rtl } from '../../utils';
import type { LabelPosition, Size } from '../types';
import type { LabelPosition, Size, PrefixSuffixWrapperProps } from '../types';

type ContainerProps = {
disabled?: boolean;
Expand Down Expand Up @@ -318,3 +318,35 @@ export const Label = (
export const LabelWrapper = styled( FlexItem )`
max-width: calc( 100% - 10px );
`;

const prefixSuffixWrapperStyles = ( {
variant = 'default',
size,
__next40pxDefaultSize,
isPrefix,
}: PrefixSuffixWrapperProps & { isPrefix?: boolean } ) => {
const { paddingLeft: padding } = getSizeConfig( {
inputSize: size,
__next40pxDefaultSize,
} );

const paddingProperty = isPrefix
? 'paddingInlineStart'
: 'paddingInlineEnd';

if ( variant === 'default' ) {
return css( {
[ paddingProperty ]: padding,
} );
}

// If variant is 'icon' or 'control'
return css( {
display: 'flex',
[ paddingProperty ]: padding - 4,
} );
};

export const PrefixSuffixWrapper = styled.div`
${ prefixSuffixWrapperStyles }
`;
29 changes: 22 additions & 7 deletions packages/components/src/input-control/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,16 +209,31 @@ export interface InputControlLabelProps {
size?: BaseProps[ 'size' ];
}

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

export type InputControlSuffixWrapperProps = {
/**
* The suffix to be inserted.
* Internal prop used to control the padding size of the wrapper.
*
* @ignore
*/
children: ReactNode;
size?: BaseProps[ 'size' ];
/**
* Internal prop used to control the padding size of the wrapper.
*
* @ignore
*/
__next40pxDefaultSize?: BaseProps[ '__next40pxDefaultSize' ];
Comment on lines +218 to +228
Copy link
Contributor

Choose a reason for hiding this comment

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

Is @ignore-ing a prop and writing "internal" in the description enough in your opinion to allow us to make future changes to it?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes. Also in this case I do consider these props to be pretty much part of the API, just set through context by InputBase for convenience.

/**
* Adjust the wrapper based on the prefix or suffix content.
*
* - `'default'`: Standard padding for text content.
* - `'icon'`: For icons.
* - `'control'`: For controls, like buttons or selects.
*
* @default 'default'
*/
variant?: 'default' | 'icon' | 'control';
};
Loading