Skip to content

Commit

Permalink
feat(text-input): add readonly variant (#8806)
Browse files Browse the repository at this point in the history
* feat(text-input): add readonly variant

* fix(text-input): correct padding-right when in readonly mode

* Update packages/react/src/components/TextInput/TextInput.js

Co-authored-by: Scott Strubberg <sstrubberg@protonmail.com>

* fix: update public api snapshot

* docs(use-normalized-props): add guidance to useNormalizedProps hook

* feat(text-input): ensure readonly takes precedence over disabled

Co-authored-by: Scott Strubberg <sstrubberg@protonmail.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Jun 17, 2021
1 parent 8813252 commit 8f6ba79
Show file tree
Hide file tree
Showing 8 changed files with 223 additions and 106 deletions.
20 changes: 17 additions & 3 deletions packages/components/src/components/text-input/_text-input.scss
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,19 @@
width: 100%;
}

.#{$prefix}--text-input__invalid-icon {
.#{$prefix}--text-input__invalid-icon,
.#{$prefix}--text-input__readonly-icon {
position: absolute;
// top/transform used to center invalid icon in IE11
top: 50%;
right: $carbon--spacing-05;
fill: $support-error;
transform: translateY(-50%);
}

.#{$prefix}--text-input__invalid-icon {
fill: $support-error;
}

.#{$prefix}--text-input__invalid-icon--warning {
fill: $support-warning;
}
Expand Down Expand Up @@ -164,7 +168,8 @@
}

.#{$prefix}--text-input--invalid,
.#{$prefix}--text-input--warning {
.#{$prefix}--text-input--warning,
.#{$prefix}--text-input-wrapper--readonly .#{$prefix}--text-input {
padding-right: $carbon--spacing-08;
}

Expand Down Expand Up @@ -370,6 +375,15 @@
flex-direction: column;
}

//-----------------------------
// Readonly
//-----------------------------

.#{$prefix}--form--fluid .#{$prefix}--text-input-wrapper--readonly,
.#{$prefix}--text-input-wrapper--readonly .#{$prefix}--text-input {
background: transparent;
}

// Windows HCM fix
.#{$prefix}--text-input--password__visibility,
// TODO: remove selector above
Expand Down
3 changes: 3 additions & 0 deletions packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -5625,6 +5625,9 @@ Map {
"placeholder": Object {
"type": "string",
},
"readonly": Object {
"type": "bool",
},
"size": Object {
"args": Array [
Array [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,11 @@ const ControlledPasswordInput = React.forwardRef(
const input = (
<>
<input
{...textInputProps({ invalid, sharedTextInputProps, errorId })}
{...textInputProps({
invalid,
sharedTextInputProps,
invalidId: errorId,
})}
data-toggle-password-visibility={type === 'password'}
/>
<button
Expand Down
79 changes: 34 additions & 45 deletions packages/react/src/components/TextInput/PasswordInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,8 @@ import React, { useContext, useEffect, useState } from 'react';
import classNames from 'classnames';
import PropTypes from 'prop-types';
import { settings } from 'carbon-components';
import {
View16,
ViewOff16,
WarningAltFilled16,
WarningFilled16,
} from '@carbon/icons-react';
import { View16, ViewOff16 } from '@carbon/icons-react';
import { useNormalizedInputProps } from '../../internal/useNormalizedInputProps';
import { textInputProps } from './util';
import { FormContext } from '../FluidForm';

Expand Down Expand Up @@ -42,19 +38,27 @@ const PasswordInput = React.forwardRef(function PasswordInput(
ref
) {
const [inputType, setInputType] = useState(type);

const normalizedProps = useNormalizedInputProps({
id,
invalid,
invalidText,
warn,
warnText,
});

const handleTogglePasswordVisibility = (event) => {
setInputType(inputType === 'password' ? 'text' : 'password');
onTogglePasswordVisibility && onTogglePasswordVisibility(event);
};
const errorId = id + '-error-msg';
const warnId = id + '-warn-msg';
const textInputClasses = classNames(
`${prefix}--text-input`,
`${prefix}--password-input`,
className,
{
[`${prefix}--text-input--light`]: light,
[`${prefix}--text-input--invalid`]: invalid,
[`${prefix}--text-input--invalid`]: normalizedProps.invalid,
[`${prefix}--text-input--warning`]: normalizedProps.warn,
[`${prefix}--text-input--${size}`]: size,
}
);
Expand Down Expand Up @@ -104,29 +108,23 @@ const PasswordInput = React.forwardRef(function PasswordInput(
const fieldWrapperClasses = classNames(
`${prefix}--text-input__field-wrapper`,
{
[`${prefix}--text-input__field-wrapper--warning`]: !invalid && warn,
[`${prefix}--text-input__field-wrapper--warning`]: normalizedProps.warn,
}
);
const iconClasses = classNames({
[`${prefix}--text-input__invalid-icon`]:
normalizedProps.invalid || normalizedProps.warn,
[`${prefix}--text-input__invalid-icon--warning`]: normalizedProps.warn,
});

const label = labelText ? (
<label htmlFor={id} className={labelClasses}>
{labelText}
</label>
) : null;

let error = null;
if (invalid) {
error = (
<div className={`${prefix}--form-requirement`} id={errorId}>
{invalidText}
</div>
);
} else if (warn) {
error = (
<div className={`${prefix}--form-requirement`} id={warnId}>
{warnText}
</div>
);
}
const helper = helperText ? (
<div className={helperTextClasses}>{helperText}</div>
) : null;

const passwordIsVisible = inputType === 'text';
const passwordVisibilityIcon = passwordIsVisible ? (
Expand All @@ -150,11 +148,11 @@ const PasswordInput = React.forwardRef(function PasswordInput(
<>
<input
{...textInputProps({
invalid,
sharedTextInputProps,
errorId,
warn,
warnId,
invalid: normalizedProps.invalid,
invalidId: normalizedProps.invalidId,
warn: normalizedProps.warn,
warnId: normalizedProps.warnId,
})}
disabled={disabled}
data-toggle-password-visibility={inputType === 'password'}
Expand All @@ -173,9 +171,6 @@ const PasswordInput = React.forwardRef(function PasswordInput(
</button>
</>
);
const helper = helperText ? (
<div className={helperTextClasses}>{helperText}</div>
) : null;

const { isFluid } = useContext(FormContext);

Expand All @@ -194,22 +189,16 @@ const PasswordInput = React.forwardRef(function PasswordInput(
</div>
)}
<div className={fieldOuterWrapperClasses}>
<div className={fieldWrapperClasses} data-invalid={invalid || null}>
{invalid && (
<WarningFilled16
className={`${prefix}--text-input__invalid-icon`}
/>
)}
{!invalid && warn && (
<WarningAltFilled16
className={`${prefix}--text-input__invalid-icon ${prefix}--text-input__invalid-icon--warning`}
/>
<div
className={fieldWrapperClasses}
data-invalid={normalizedProps.invalid || null}>
{normalizedProps.icon && (
<normalizedProps.icon className={iconClasses} />
)}
{input}
{isFluid && !inline && error}
{isFluid && !inline && normalizedProps.validation}
</div>
{!isFluid && error}
{!invalid && !warn && !isFluid && !inline && helper}
{!isFluid && !inline && (normalizedProps.validation || helper)}
</div>
</div>
);
Expand Down
11 changes: 8 additions & 3 deletions packages/react/src/components/TextInput/TextInput-story.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ const ControlledPasswordInputApp = React.forwardRef(
);

const props = {
TextInputProps: () => ({
SharedInputProps: () => ({
className: 'some-class',
id: 'test2',
defaultValue: text(
Expand Down Expand Up @@ -80,6 +80,9 @@ const props = {
onClick: action('onClick'),
onChange: action('onChange'),
}),
TextInputProps: () => ({
readonly: boolean('Readonly variant (readonly)', false),
}),
PasswordInputProps: () => ({
tooltipPosition: select(
'Tooltip position (tooltipPosition)',
Expand Down Expand Up @@ -123,6 +126,7 @@ export default {
export const Default = () => (
<TextInput
type={select('Form control type (type)', types, 'text')}
{...props.SharedInputProps()}
{...props.TextInputProps()}
/>
);
Expand All @@ -142,6 +146,7 @@ export const Fluid = () => (
<FluidForm>
<TextInput
type={select('Form control type (type)', types, 'text')}
{...props.SharedInputProps()}
{...props.TextInputProps()}
/>
</FluidForm>
Expand All @@ -161,7 +166,7 @@ Fluid.parameters = {
export const TogglePasswordVisibility = () => {
return (
<TextInput.PasswordInput
{...props.TextInputProps()}
{...props.SharedInputProps()}
{...props.PasswordInputProps()}
/>
);
Expand All @@ -187,7 +192,7 @@ export const FullyControlledTogglePasswordVisibility = () => {

return (
<ControlledPasswordInputApp
{...props.TextInputProps()}
{...props.SharedInputProps()}
{...props.PasswordInputProps()}
/>
);
Expand Down
Loading

0 comments on commit 8f6ba79

Please sign in to comment.