Skip to content

Commit ae0a179

Browse files
authored
TextField - add 'clearButton' and 'topTrailingAccessory' props (#3113)
* TextField - add clear button * demo * adding 'topTrailingAccessory' prop and fix assets * replacing icon * fix validationIcon with no validation massage * fix topmargin * revert * fix master merge * Delete demo/src/assets/Assets.ts remove Demo/Assets * add preset * add icons * adding size * add testIDs * remove console * pr comments fixes * fix testID * pr comments fix
1 parent e7a333b commit ae0a179

File tree

14 files changed

+167
-31
lines changed

14 files changed

+167
-31
lines changed

demo/src/configurations.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ export const loadDemoConfigurations = () => {
1414
refresh: require('./assets/icons/refresh.png'),
1515
search: require('./assets/icons/search.png'),
1616
settings: require('./assets/icons/settings.png'),
17-
share: require('./assets/icons/share.png')
17+
share: require('./assets/icons/share.png'),
18+
info: require('./assets/icons/info.png'),
19+
exclamation: require('./assets/icons/exclamationFillSmall.png')
1820
});
1921

2022
Assets.loadAssetsGroup('images.demo', {

demo/src/screens/componentScreens/TextFieldScreen.tsx

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React, {Component} from 'react';
22
import {ScrollView, ActivityIndicator} from 'react-native';
33
import {
4+
Assets,
45
Colors,
56
Spacings,
67
View,
@@ -12,11 +13,10 @@ import {
1213
FieldContextType,
1314
TextFieldProps,
1415
SegmentedControl,
15-
Assets
16+
Icon
1617
} from 'react-native-ui-lib';
1718
const {KeyboardAwareInsetsView} = Keyboard;
1819
const priceFormatter = Intl.NumberFormat('en-US');
19-
const validationIcon = require('../../assets/icons/exclamationFillSmall.png');
2020

2121
export default class TextFieldScreen extends Component {
2222
input = React.createRef<TextFieldRef>();
@@ -167,7 +167,7 @@ export default class TextFieldScreen extends Component {
167167
};
168168

169169
renderValidationExample() {
170-
const {errorPosition} = this.state;
170+
const {errorPosition, preset} = this.state;
171171

172172
return (
173173
<>
@@ -178,7 +178,7 @@ export default class TextFieldScreen extends Component {
178178
<SegmentedControl segments={[{label: 'Bottom'}, {label: 'Top'}]} onChangeIndex={this.onChangeIndexValidation}/>
179179
</View>
180180
</View>
181-
181+
182182
<TextField
183183
value={this.state.value}
184184
onChangeText={value => this.setState({value})}
@@ -204,7 +204,9 @@ export default class TextFieldScreen extends Component {
204204
containerStyle={{flex: 1}}
205205
validationMessagePosition={errorPosition}
206206
helperText={'Enter first and last name'}
207-
validationIcon={{source: validationIcon, style: {marginTop: 1}}}
207+
validationIcon={{source: Assets.icons.demo.exclamation, style: {marginTop: 1}}}
208+
topTrailingAccessory={<Icon source={Assets.icons.demo.info} size={16}/>}
209+
preset={preset}
208210
/>
209211
<Button
210212
outline
@@ -351,6 +353,26 @@ export default class TextFieldScreen extends Component {
351353
);
352354
}
353355

356+
renderClearButtonExample() {
357+
return (
358+
<>
359+
<Text h3 marginB-s3>
360+
Clear Button
361+
</Text>
362+
363+
<TextField
364+
label="Description"
365+
placeholder="Enter text..."
366+
showClearButton
367+
value={this.state.value}
368+
onChangeText={value => this.setState({value})}
369+
trailingAccessory={<Icon source={Assets.icons.demo.search}/>}
370+
// multiline
371+
/>
372+
</>
373+
);
374+
}
375+
354376
renderHintExample() {
355377
return (
356378
<>
@@ -414,15 +436,16 @@ export default class TextFieldScreen extends Component {
414436

415437
render() {
416438
return (
417-
<ScrollView keyboardShouldPersistTaps="always" showsVerticalScrollIndicator={false}>
418-
<View flex padding-page>
439+
<ScrollView showsVerticalScrollIndicator={false} keyboardShouldPersistTaps="always">
440+
<View padding-page>
419441
<Text h1 marginB-s4>TextField</Text>
420442

421443
{this.renderDefaultExample()}
422444
{this.renderPresetExample()}
423445
{this.renderPlaceholdersExample()}
424446
{this.renderValidationExample()}
425447
{this.renderHintExample()}
448+
{this.renderClearButtonExample()}
426449
{this.renderCherCounterExample()}
427450
{this.renderAccessoriesExample()}
428451
{this.renderStateColorsExample()}

src/assets/icons/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,8 @@ export const icons = {
1616
},
1717
get x() {
1818
return require('./x.png');
19+
},
20+
get xFlat() {
21+
return require('./xFlat.png');
1922
}
2023
};

src/assets/icons/xFlat.png

617 Bytes
Loading

src/assets/icons/[email protected]

850 Bytes
Loading

src/assets/icons/[email protected]

983 Bytes
Loading

src/assets/icons/[email protected]

1.45 KB
Loading

src/assets/icons/[email protected]

1.99 KB
Loading

src/components/textField/CharCounter.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1+
import _ from 'lodash';
12
import React, {useContext} from 'react';
23
import {StyleSheet} from 'react-native';
3-
import _ from 'lodash';
44
import Text from '../text';
55
import FieldContext from './FieldContext';
66
import {CharCounterProps} from './types';
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import React, {useContext, useCallback, useMemo} from 'react';
2+
import {StyleSheet} from 'react-native';
3+
import {useAnimatedStyle, useSharedValue, withSpring} from 'react-native-reanimated';
4+
import {useDidUpdate} from '../../hooks';
5+
import Assets from '../../assets';
6+
import {Spacings, Colors} from '../../style';
7+
import View from '../view';
8+
import Button from '../button';
9+
import FieldContext from './FieldContext';
10+
import {TextFieldProps} from './types';
11+
12+
const hitSlop = {top: 20, bottom: 20, left: 20, right: 20};
13+
const NON_VISIBLE_POSITION = 100;
14+
const VISIBLE_POSITION = 0;
15+
const SPRING_ANIMATION_CONFIG = {velocity: 300, damping: 20, stiffness: 300, mass: 0.8};
16+
17+
const ClearButton = ({testID, onClear, onChangeText}: Pick<TextFieldProps, 'onClear' | 'testID' | 'onChangeText'>) => {
18+
const {hasValue} = useContext(FieldContext);
19+
const animatedValue = useSharedValue(hasValue ? VISIBLE_POSITION : NON_VISIBLE_POSITION);
20+
21+
// @ts-expect-error should be fixed in version 3.5 (https://github.com/software-mansion/react-native-reanimated/pull/4881)
22+
const animatedStyle = useAnimatedStyle(() => {
23+
return {
24+
transform: [{translateX: animatedValue.value}, {translateY: 0}]
25+
};
26+
});
27+
28+
const style = useMemo(() => [styles.container, animatedStyle], [animatedStyle]);
29+
30+
const animate = useCallback(() => {
31+
const toValue = hasValue ? VISIBLE_POSITION : NON_VISIBLE_POSITION;
32+
animatedValue.value = withSpring(toValue, SPRING_ANIMATION_CONFIG);
33+
},
34+
[animatedValue, hasValue]);
35+
36+
useDidUpdate(() => {
37+
animate();
38+
}, [hasValue, animate]);
39+
40+
const clear = () => {
41+
onChangeText?.('');
42+
onClear?.();
43+
};
44+
45+
return (
46+
//@ts-expect-error should be fixed in version 3.5 (https://github.com/software-mansion/react-native-reanimated/pull/4881)
47+
<View reanimated style={style}>
48+
<Button
49+
link
50+
iconSource={Assets.icons.xFlat}
51+
iconStyle={styles.clearIcon}
52+
onPress={clear}
53+
hitSlop={hitSlop}
54+
accessible={hasValue}
55+
accessibilityLabel={'clear'}
56+
testID={testID}
57+
/>
58+
</View>
59+
);
60+
};
61+
62+
const styles = StyleSheet.create({
63+
container: {
64+
marginHorizontal: Spacings.s3
65+
},
66+
clearIcon: {
67+
tintColor: Colors.$textNeutralLight
68+
}
69+
});
70+
71+
export default ClearButton;

0 commit comments

Comments
 (0)