Skip to content

Commit da520fc

Browse files
yihuiliaoyihuiliaosnowystinger
authored
Readonly textfield textarea (#3277)
* started textarea/textfield read only * updated css for focus and fixed spacing for text area * focus state like isQuiet, removed validation/icons, removed helptext * hopefully(?) fixed linting errors * linting errors hopefully fixed pt 2 * created new component called ReadOnlyField * added default value to readonly * fixed default value * fix bug with readOnly conditional * fixed the issue with inputValue and conditional statement for rendering when readOnly * fixed focus ring by moving it to readonlyfield * implemented changes based on feedback * fixed height/spacing issues and added additional stories using controls * added controls to specific stories and fixed internationalization * fixed stories, formatting, made changes to field/readonlyfield * removed flex in readonlyfield and adjusted logic for height change in textarea * fixed issue with textarea height change and readOnly * fix textarea conditional with onHeightChange Co-authored-by: yihuiliao <[email protected]> Co-authored-by: Robert Snow <[email protected]>
1 parent a5a9d95 commit da520fc

File tree

13 files changed

+284
-21
lines changed

13 files changed

+284
-21
lines changed

packages/@adobe/spectrum-css-temp/components/textfield/index.css

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,12 @@ governing permissions and limitations under the License.
3535
min-width: var(--spectrum-textfield-min-width);
3636
width: var(--spectrum-component-single-line-width);
3737

38-
&:not(.spectrum-Textfield--quiet).spectrum-Textfield--multiline .spectrum-Textfield-input:not(:disabled) {
38+
&:not(.spectrum-Textfield--quiet, .spectrum-Textfield--readonly).spectrum-Textfield--multiline .spectrum-Textfield-input:not(:disabled){
3939
resize: vertical;
4040
}
4141

42-
&.spectrum-Textfield--quiet.spectrum-Textfield--multiline .spectrum-Textfield-input {
42+
&.spectrum-Textfield--quiet.spectrum-Textfield--multiline .spectrum-Textfield-input,
43+
&.spectrum-Textfield--readonly .spectrum-Textfield-input {
4344
height: var(--spectrum-textfield-height);
4445
min-height: var(--spectrum-textfield-height);
4546
overflow-x: hidden;
@@ -259,7 +260,6 @@ governing permissions and limitations under the License.
259260
}
260261
}
261262

262-
263263
/* same positioning as invalid icon */
264264
.spectrum-Textfield--loadable .spectrum-Textfield-circleLoader {
265265
position: absolute;

packages/@adobe/spectrum-css-temp/components/textfield/skin.css

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,10 @@ governing permissions and limitations under the License.
130130
}
131131
}
132132

133+
.spectrum-Textfield--readonly & {
134+
border-color: var(--spectrum-textfield-quiet-background-color);
135+
}
136+
133137
.spectrum-Textfield.spectrum-Textfield--invalid & {
134138
border-color: var(--spectrum-textfield-border-color-error);
135139

@@ -148,7 +152,6 @@ governing permissions and limitations under the License.
148152
}
149153
}
150154
}
151-
152155
}
153156

154157
.spectrum-Textfield-icon {
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
22
"(optional)": "(اختياري)",
3-
"(required)": "(مطلوب)"
3+
"(required)": "(مطلوب)",
4+
"(None)": "(None)"
45
}
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
22
"(required)": "(required)",
3-
"(optional)": "(optional)"
3+
"(optional)": "(optional)",
4+
"(None)": "(None)"
45
}

packages/@react-spectrum/label/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@
3232
},
3333
"dependencies": {
3434
"@babel/runtime": "^7.6.2",
35+
"@react-aria/focus": "^3.6.1",
3536
"@react-aria/i18n": "^3.4.1",
37+
"@react-aria/interactions": "^3.9.1",
3638
"@react-aria/label": "^3.3.1",
3739
"@react-aria/utils": "^3.13.1",
3840
"@react-spectrum/form": "^3.3.1",

packages/@react-spectrum/label/src/Field.tsx

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,17 @@
1313
import {classNames, useStyleProps} from '@react-spectrum/utils';
1414
import {Flex} from '@react-spectrum/layout';
1515
import {HelpText} from './HelpText';
16+
// @ts-ignore
17+
import intlMessages from '../intl/*.json';
1618
import {Label} from './Label';
1719
import {LabelPosition} from '@react-types/shared';
1820
import labelStyles from '@adobe/spectrum-css-temp/components/fieldlabel/vars.css';
1921
import {mergeProps, mergeRefs} from '@react-aria/utils';
2022
import React, {RefObject} from 'react';
23+
import {ReadOnlyField} from './ReadOnlyField';
2124
import {SpectrumFieldProps} from '@react-types/label';
2225
import {useFormProps} from '@react-spectrum/form';
26+
import {useMessageFormatter} from '@react-aria/i18n';
2327

2428
function Field(props: SpectrumFieldProps, ref: RefObject<HTMLElement>) {
2529
props = useFormProps(props);
@@ -37,6 +41,10 @@ function Field(props: SpectrumFieldProps, ref: RefObject<HTMLElement>) {
3741
showErrorIcon,
3842
children,
3943
labelProps,
44+
readOnlyText,
45+
isReadOnly,
46+
inputProps,
47+
inputRef,
4048
// Not every component that uses <Field> supports help text.
4149
descriptionProps = {},
4250
errorMessageProps = {},
@@ -47,6 +55,8 @@ function Field(props: SpectrumFieldProps, ref: RefObject<HTMLElement>) {
4755
} = props;
4856
let {styleProps} = useStyleProps(otherProps);
4957
let hasHelpText = !!description || errorMessage && validationState === 'invalid';
58+
let formatMessage = useMessageFormatter(intlMessages);
59+
let displayReadOnly = isReadOnly && (readOnlyText || readOnlyText === '');
5060

5161
if (label || hasHelpText) {
5262
let labelWrapperClass = classNames(
@@ -78,10 +88,23 @@ function Field(props: SpectrumFieldProps, ref: RefObject<HTMLElement>) {
7888
showErrorIcon={showErrorIcon} />
7989
);
8090

91+
if (displayReadOnly) {
92+
if (readOnlyText === '') {
93+
readOnlyText = formatMessage('(None)');
94+
}
95+
children = (
96+
<ReadOnlyField
97+
{...props}
98+
readOnlyText={readOnlyText}
99+
inputProps={inputProps}
100+
ref={inputRef as RefObject<HTMLTextAreaElement>} />
101+
);
102+
}
103+
81104
let renderChildren = () => (
82105
<Flex direction="column" UNSAFE_className={classNames(labelStyles, 'spectrum-Field-wrapper')}>
83106
{children}
84-
{hasHelpText && renderHelpText()}
107+
{hasHelpText && !displayReadOnly && renderHelpText()}
85108
</Flex>
86109
);
87110

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* Copyright 2020 Adobe. All rights reserved.
3+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License. You may obtain a copy
5+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
6+
*
7+
* Unless required by applicable law or agreed to in writing, software distributed under
8+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9+
* OF ANY KIND, either express or implied. See the License for the specific language
10+
* governing permissions and limitations under the License.
11+
*/
12+
13+
import {classNames} from '@react-spectrum/utils';
14+
import {FocusRing} from '@react-aria/focus';
15+
import {mergeProps} from '@react-aria/utils';
16+
import React, {RefObject, useCallback} from 'react';
17+
import {SpectrumFieldProps} from '@react-types/label';
18+
import styles from '@adobe/spectrum-css-temp/components/textfield/vars.css';
19+
import {useFormProps} from '@react-spectrum/form';
20+
import {useHover} from '@react-aria/interactions';
21+
import {useLayoutEffect} from '@react-aria/utils';
22+
23+
function ReadOnlyField(props: SpectrumFieldProps, ref: RefObject<HTMLTextAreaElement>) {
24+
props = useFormProps(props);
25+
let {
26+
isDisabled,
27+
readOnlyText,
28+
inputProps,
29+
autoFocus
30+
} = props;
31+
delete inputProps.defaultValue;
32+
let {hoverProps, isHovered} = useHover({isDisabled});
33+
34+
let onHeightChange = useCallback(() => {
35+
let input = ref.current;
36+
let prevAlignment = input.style.alignSelf;
37+
input.style.alignSelf = 'start';
38+
input.style.height = 'auto';
39+
input.style.height = `${input.scrollHeight}px`;
40+
input.style.alignSelf = prevAlignment;
41+
}, [ref]);
42+
43+
useLayoutEffect(() => {
44+
if (ref.current) {
45+
onHeightChange();
46+
}
47+
}, [onHeightChange, readOnlyText, ref]);
48+
49+
return (
50+
<div
51+
className={
52+
classNames(
53+
styles,
54+
'spectrum-Textfield',
55+
{
56+
'spectrum-Textfield--quiet': true,
57+
'spectrum-Textfield--readonly': true
58+
}
59+
)
60+
}>
61+
<FocusRing focusRingClass={classNames(styles, 'focus-ring')} isTextInput autoFocus={autoFocus}>
62+
<textarea
63+
{...mergeProps(inputProps, hoverProps)}
64+
rows={1}
65+
ref={ref}
66+
value={readOnlyText}
67+
className={
68+
classNames(
69+
styles,
70+
'spectrum-Textfield-input',
71+
{
72+
'is-hovered': isHovered
73+
}
74+
)
75+
} />
76+
</FocusRing>
77+
</div>
78+
);
79+
}
80+
81+
let _ReadOnlyField = React.forwardRef(ReadOnlyField);
82+
export {_ReadOnlyField as ReadOnlyField};

packages/@react-spectrum/textfield/src/TextArea.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,22 +28,22 @@ function TextArea(props: SpectrumTextFieldProps, ref: RefObject<TextFieldRef>) {
2828
onChange,
2929
...otherProps
3030
} = props;
31-
3231
// not in stately because this is so we know when to re-measure, which is a spectrum design
3332
let [inputValue, setInputValue] = useControlledState(props.value, props.defaultValue, () => {});
3433

34+
let inputText = props.value ?? props.defaultValue ?? '';
3535
let inputRef = useRef<HTMLTextAreaElement>();
3636

3737
let onHeightChange = useCallback(() => {
38-
if (isQuiet) {
38+
if (isQuiet && !isReadOnly) {
3939
let input = inputRef.current;
4040
let prevAlignment = input.style.alignSelf;
4141
input.style.alignSelf = 'start';
4242
input.style.height = 'auto';
4343
input.style.height = `${input.scrollHeight}px`;
4444
input.style.alignSelf = prevAlignment;
4545
}
46-
}, [isQuiet, inputRef]);
46+
}, [isQuiet, isReadOnly, inputRef]);
4747

4848
useLayoutEffect(() => {
4949
if (inputRef.current) {
@@ -74,7 +74,8 @@ function TextArea(props: SpectrumTextFieldProps, ref: RefObject<TextFieldRef>) {
7474
isDisabled={isDisabled}
7575
isQuiet={isQuiet}
7676
isReadOnly={isReadOnly}
77-
isRequired={isRequired} />
77+
isRequired={isRequired}
78+
readOnlyText={inputText} />
7879
);
7980
}
8081

packages/@react-spectrum/textfield/src/TextField.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,14 @@ import {useTextField} from '@react-aria/textfield';
1818

1919
function TextField(props: SpectrumTextFieldProps, ref: RefObject<TextFieldRef>) {
2020
props = useProviderProps(props);
21-
21+
let inputText = props.value ?? props.defaultValue ?? '';
2222
let inputRef = useRef<HTMLInputElement>();
2323
let {labelProps, inputProps, descriptionProps, errorMessageProps} = useTextField(props, inputRef);
2424

2525
if (props.placeholder) {
2626
console.warn('Placeholders are deprecated due to accessibility issues. Please use help text instead. See the docs for details: https://react-spectrum.adobe.com/react-spectrum/TextField.html#help-text');
2727
}
28-
28+
2929
return (
3030
<TextFieldBase
3131
{...props}
@@ -34,7 +34,8 @@ function TextField(props: SpectrumTextFieldProps, ref: RefObject<TextFieldRef>)
3434
descriptionProps={descriptionProps}
3535
errorMessageProps={errorMessageProps}
3636
ref={ref}
37-
inputRef={inputRef} />
37+
inputRef={inputRef}
38+
readOnlyText={inputText} />
3839
);
3940
}
4041

packages/@react-spectrum/textfield/src/TextFieldBase.tsx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ interface TextFieldBaseProps extends Omit<SpectrumTextFieldProps, 'onChange'>, P
3333
errorMessageProps?: HTMLAttributes<HTMLElement>,
3434
inputRef?: RefObject<HTMLInputElement | HTMLTextAreaElement>,
3535
loadingIndicator?: ReactElement,
36-
isLoading?: boolean
36+
isLoading?: boolean,
37+
readOnlyText?: string
3738
}
3839

3940
function TextFieldBase(props: TextFieldBaseProps, ref: Ref<TextFieldRef>) {
@@ -54,13 +55,14 @@ function TextFieldBase(props: TextFieldBaseProps, ref: Ref<TextFieldRef>) {
5455
inputRef,
5556
isLoading,
5657
loadingIndicator,
57-
validationIconClassName
58+
validationIconClassName,
59+
readOnlyText
5860
} = props;
5961
let {hoverProps, isHovered} = useHover({isDisabled});
6062
let domRef = useRef<HTMLDivElement>(null);
6163
let defaultInputRef = useRef<HTMLInputElement | HTMLTextAreaElement>(null);
6264
inputRef = inputRef || defaultInputRef;
63-
65+
6466
// Expose imperative interface for ref
6567
useImperativeHandle(ref, () => ({
6668
...createFocusableRef(domRef, inputRef),
@@ -143,15 +145,17 @@ function TextFieldBase(props: TextFieldBaseProps, ref: Ref<TextFieldRef>) {
143145
className: multiLine ? 'spectrum-Field-field--multiline' : ''
144146
}));
145147
}
146-
148+
147149
return (
148150
<Field
149151
{...props}
150152
labelProps={labelProps}
151153
descriptionProps={descriptionProps}
152154
errorMessageProps={errorMessageProps}
153155
showErrorIcon={false}
154-
ref={domRef}>
156+
ref={domRef}
157+
readOnlyText={readOnlyText}
158+
inputProps={inputProps}>
155159
{textField}
156160
</Field>
157161
);

0 commit comments

Comments
 (0)