woocommerce/packages/js/components/src/text-control-with-affixes/index.js

168 lines
3.9 KiB
JavaScript
Raw Normal View History

/**
* External dependencies
*/
Allow packages to be built in isolation. (https://github.com/woocommerce/woocommerce-admin/pull/7286) * Use yarn instead of npm. In prep for workspaces, since we're locked to npm < 7. See: https://github.com/woocommerce/woocommerce-admin/pull/7126#issue-661287749 * Initial workspace creation. * Add initial tsc build to @woocommerce/number. * Attempt to build experimental package. * Try currency package. * Define all packages as workspaces. * Use tsconfig common to packages. * Fix currency package build. * Build csv-export with tsc. * Try to build customer-effort-score with tsc. * Fix JSX pragma. * Build data package with tsc. * Build date package with tsc. * Build experimental package with tsc. * Try to build explat package with tsc. * Build navigation package with tsc. * Build notices package with tsc. * Build onboarding package with tsc. * Build components package with tsc. * Swap in package JS build into main script. * Fix experimental package build. * Try per-package css build with components. * Try to run components package tests in isolation. Broken on JSX in test files not being transformed. * Move @woocommerce/wc-admin-settings into a package. * Try to fix components package tests. Fails because we aren't setting up the jest/jest-dom globals. * Move JS test code to reusable (private) package. * Enable incremental TS builds. * Use workspaces to run JS tests. * Use new jest configs for update snapshot scripts. * Fix style builds. * Fix package version in components. * Fix client test debug and watch scripts. * Update yarn lock. * Update test-staged behavior. * Try to fix storybook. * Fix storybook. * Update more npm commands to yarn. * Add changelog. * Fix lint errors. * Update packages readme script references. * Clean up unused gitignore match. * Fix another npm command. * Fix JS builds on watch. * Fix start script. * Fix start scripts for packages. * Use tsc to build packages before tests * yarn -> npm. # Conflicts: # package-lock.json # package.json * Fix linter error. * Remove workspace definitions. * Fix missing Fragment import. * Fix package lock. * Fix missing reference. * Only build commonjs module for js-tests helper. * Remove errant dependency from components. * Remove noop scripts. * Fix package JS build before testing. * Revert noisy formatting changes. * Fix precommit and test scripts. * Fix minimum expected recommended extension count. Japan test case breaks this. * Revert babel config changes. * chore(release): publish - @woocommerce/components@7.2.0 - @woocommerce/csv-export@1.4.0 - @woocommerce/currency@3.2.0 - @woocommerce/customer-effort-score@1.1.0 - @woocommerce/data@1.4.0 - @woocommerce/date@3.1.0 - @woocommerce/dependency-extraction-webpack-plugin@1.7.0 - @woocommerce/eslint-plugin@1.3.0 - @woocommerce/experimental@1.5.0 - @woocommerce/explat@1.1.0 - @woocommerce/js-tests@1.1.0 - @woocommerce/navigation@6.1.0 - @woocommerce/notices@3.1.0 - @woocommerce/number@2.2.0 - @woocommerce/onboarding@1.1.0 - @woocommerce/tracks@1.1.0 - @woocommerce/wc-admin-settings@1.1.0 * Add script for running 'start' in a package. * Remove yarn from gitignore. * Update package changelogs, prep versions for release. * Try to fix E2E tests after main merge. * Some cleanup. * Add changelog. Co-authored-by: Paul Sealock <psealock@gmail.com>
2021-07-14 20:38:57 +00:00
import { createElement, Component } from '@wordpress/element';
import { compose, withInstanceId } from '@wordpress/compose';
import PropTypes from 'prop-types';
import { BaseControl, withFocusOutside } from '@wordpress/components';
import classnames from 'classnames';
/**
* This component is essentially a wrapper (really a reimplementation) around the
* TextControl component that adds support for affixes, i.e. the ability to display
* a fixed part either at the beginning or at the end of the text input.
*/
class TextControlWithAffixes extends Component {
constructor( props ) {
super( props );
this.state = {
isFocused: false,
};
}
handleFocusOutside() {
this.setState( { isFocused: false } );
}
handleOnClick( event, onClick ) {
this.setState( { isFocused: true } );
if ( typeof onClick === 'function' ) {
onClick( event );
}
}
render() {
const {
label,
value,
help,
className,
instanceId,
onChange,
onClick,
prefix,
suffix,
type,
disabled,
...props
} = this.props;
const { isFocused } = this.state;
const id = `inspector-text-control-with-affixes-${ instanceId }`;
const onChangeValue = ( event ) => onChange( event.target.value );
const describedby = [];
if ( help ) {
describedby.push( `${ id }__help` );
}
if ( prefix ) {
describedby.push( `${ id }__prefix` );
}
if ( suffix ) {
describedby.push( `${ id }__suffix` );
}
const baseControlClasses = classnames( className, {
'with-value': value !== '',
empty: value === '',
active: isFocused && ! disabled,
} );
const affixesClasses = classnames( 'text-control-with-affixes', {
'text-control-with-prefix': prefix,
'text-control-with-suffix': suffix,
disabled,
} );
return (
<BaseControl
label={ label }
id={ id }
help={ help }
className={ baseControlClasses }
onClick={ ( event ) => this.handleOnClick( event, onClick ) }
>
<div className={ affixesClasses }>
{ prefix && (
<span
id={ `${ id }__prefix` }
className="text-control-with-affixes__prefix"
>
{ prefix }
</span>
) }
<input
className="components-text-control__input"
type={ type }
id={ id }
value={ value }
onChange={ onChangeValue }
aria-describedby={ describedby.join( ' ' ) }
disabled={ disabled }
onFocus={ () => this.setState( { isFocused: true } ) }
{ ...props }
/>
{ suffix && (
<span
id={ `${ id }__suffix` }
className="text-control-with-affixes__suffix"
>
{ suffix }
</span>
) }
</div>
</BaseControl>
);
}
}
TextControlWithAffixes.defaultProps = {
type: 'text',
};
TextControlWithAffixes.propTypes = {
/**
* If this property is added, a label will be generated using label property as the content.
*/
label: PropTypes.string,
/**
* If this property is added, a help text will be generated using help property as the content.
*/
help: PropTypes.string,
/**
* Type of the input element to render. Defaults to "text".
*/
type: PropTypes.string,
/**
* The current value of the input.
*/
value: PropTypes.string.isRequired,
/**
* The class that will be added with "components-base-control" to the classes of the wrapper div.
* If no className is passed only components-base-control is used.
*/
className: PropTypes.string,
/**
* A function that receives the value of the input.
*/
onChange: PropTypes.func.isRequired,
/**
* Markup to be inserted at the beginning of the input.
*/
prefix: PropTypes.node,
/**
* Markup to be appended at the end of the input.
*/
suffix: PropTypes.node,
/**
* Whether or not the input is disabled.
*/
disabled: PropTypes.bool,
};
export default compose( [
withInstanceId,
withFocusOutside, // this MUST be the innermost HOC as it calls handleFocusOutside
] )( TextControlWithAffixes );