From 9086f2eac31e9ec4298ab3f4ec2aa15bd9b543e2 Mon Sep 17 00:00:00 2001 From: Adi Date: Wed, 15 Feb 2023 15:32:28 +0200 Subject: [PATCH 1/3] Migrate ChipsInput to Incubator ChipsInput --- demo/src/index.js | 3 - demo/src/screens/MenuStructure.js | 3 +- .../componentScreens/ChipsInputScreen.tsx | 176 ------------------ demo/src/screens/componentScreens/index.js | 1 - src/index.ts | 2 +- 5 files changed, 2 insertions(+), 183 deletions(-) delete mode 100644 demo/src/screens/componentScreens/ChipsInputScreen.tsx diff --git a/demo/src/index.js b/demo/src/index.js index 2879e7d3bd..9a6b06c717 100644 --- a/demo/src/index.js +++ b/demo/src/index.js @@ -57,9 +57,6 @@ module.exports = { get ExpandableSectionScreen() { return require('./screens/componentScreens/ExpandableSectionScreen').default; }, - get ChipsInputScreen() { - return require('./screens/componentScreens/ChipsInputScreen').default; - }, get HapticScreen() { return require('./screens/componentScreens/HapticScreen').default; }, diff --git a/demo/src/screens/MenuStructure.js b/demo/src/screens/MenuStructure.js index 2c4ae9d31b..2e8cc3f843 100644 --- a/demo/src/screens/MenuStructure.js +++ b/demo/src/screens/MenuStructure.js @@ -71,7 +71,6 @@ export const navigationData = { {title: 'Stepper', tags: 'stepper form', screen: 'unicorn.components.StepperScreen'}, {title: 'Slider', tags: 'slider', screen: 'unicorn.components.SliderScreen'}, {title: 'Switch', tags: 'switch toggle', screen: 'unicorn.components.SwitchScreen'}, - {title: 'ChipsInput', tags: 'chips tags input form', screen: 'unicorn.components.ChipsInputScreen'}, {title: 'Masked Inputs', tags: 'text input form mask', screen: 'unicorn.components.MaskedInputScreen'} ] }, @@ -175,7 +174,7 @@ export const navigationData = { title: 'Incubator (Experimental)', screens: [ {title: 'Calendar', tags: 'calendar', screen: 'unicorn.components.IncubatorCalendarScreen'}, - {title: 'ChipsInput (New)', tags: 'chips input', screen: 'unicorn.components.IncubatorChipsInputScreen'}, + {title: 'ChipsInput', tags: 'chips input', screen: 'unicorn.components.IncubatorChipsInputScreen'}, {title: 'Native TouchableOpacity', tags: 'touchable native', screen: 'unicorn.incubator.TouchableOpacityScreen'}, {title: 'Dialog (New)', tags: 'dialog modal popup alert', screen: 'unicorn.incubator.IncubatorDialogScreen'}, {title: 'TextField (New)', tags: 'text field input', screen: 'unicorn.components.IncubatorTextFieldScreen'}, diff --git a/demo/src/screens/componentScreens/ChipsInputScreen.tsx b/demo/src/screens/componentScreens/ChipsInputScreen.tsx deleted file mode 100644 index ad3ce81be0..0000000000 --- a/demo/src/screens/componentScreens/ChipsInputScreen.tsx +++ /dev/null @@ -1,176 +0,0 @@ -import React, {Component} from 'react'; -import {StyleSheet, ScrollView} from 'react-native'; -import {View, Colors, Text, Typography, ChipsInput, ChipsInputChipProps} from 'react-native-ui-lib'; // eslint-disable-line - -interface State { - chips: Array; - namesChips: Array; - nonRemovalChips: Array; - customChips: Array; - tags: Array; - tags2: Array; - tags3: Array; -} - -export default class ChipsInputScreen extends Component<{}, State> { - // @ts-ignore - customChipsInput = React.createRef(); - - constructor(props: any) { - super(props); - - this.state = { - chips: [{label: 'Falcon 9'}, {label: 'Enterprise'}, {label: 'Challenger', borderRadius: 0}, {label: 'Coca Cola', invalid: true}], - namesChips: [{label: 'Amit'}, {label: 'Ethan', invalid: true}], - nonRemovalChips: [{label: 'Non'}, {label: 'Removable'}, {label: 'Tags'}], - customChips: ['Chips', 'Input'], - tags: [{label: 'Amit'}, {label: 'Ethan', invalid: true}], - tags2: ['Non', 'Removable', 'Tags'], - tags3: ['Change', 'Typography'] - }; - } - - onTagPress = (tagIndex: number, markedTagIndex: number) => { - this.customChipsInput.current?.markTagIndex(tagIndex === markedTagIndex ? undefined : tagIndex); - }; - - renderCustomTag(tag: any, _: number, shouldMarkToRemove: boolean) { - return ( - - {tag.label} - - ); - } - - renderLeftElement = () => { - return ( - - - To: - - - ); - }; - - renderSearchTypeInput = () => { - return ( - <> - Search Type - - - - - ); - }; - - renderFormTypeInput = () => { - return ( - - Form Type - - - ); - }; - - onCreateTag = (value: string) => { - return {label: value}; - } - - render() { - return ( - - - - ChipsInput - - - - {this.renderSearchTypeInput()} - - {this.renderFormTypeInput()} - - - - - - Old Usage - - - - - - - - - ); - } -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - padding: 15 - }, - customInput: { - ...Typography.text60, - color: Colors.blue30 - }, - bottomMargin: { - marginBottom: 25 - }, - customTag: { - backgroundColor: Colors.purple30, - paddingVertical: 2, - paddingHorizontal: 8, - borderRadius: 3, - marginRight: 10, - marginBottom: 10 - } -}); diff --git a/demo/src/screens/componentScreens/index.js b/demo/src/screens/componentScreens/index.js index 3c502c98f5..92bfbb30b0 100644 --- a/demo/src/screens/componentScreens/index.js +++ b/demo/src/screens/componentScreens/index.js @@ -13,7 +13,6 @@ export function registerScreens(registrar) { registrar('unicorn.components.CarouselVerticalScreen', () => require('./CarouselVerticalScreen').default); registrar('unicorn.components.CheckboxScreen', () => require('./CheckboxScreen').default); registrar('unicorn.components.ChipScreen', () => require('./ChipScreen').default); - registrar('unicorn.components.ChipsInputScreen', () => require('./ChipsInputScreen').default); registrar('unicorn.components.ColorPickerScreen', () => require('./ColorPickerScreen').default); registrar('unicorn.components.ColorSwatchScreen', () => require('./ColorSwatchScreen').default); registrar('unicorn.components.ConnectionStatusBar', () => require('./ConnectionStatusBarScreen').default); diff --git a/src/index.ts b/src/index.ts index 634fd2982d..52ac56cf20 100644 --- a/src/index.ts +++ b/src/index.ts @@ -57,7 +57,7 @@ export {default as Button, ButtonProps, ButtonSize, ButtonAnimationDirection} fr export {default as Card, CardProps, CardSectionProps, CardSelectionOptions} from './components/card'; export {default as Carousel, CarouselProps, PageControlPosition} from './components/carousel'; export {default as Checkbox, CheckboxProps} from './components/checkbox'; -export {default as ChipsInput, ChipsInputProps, ChipsInputChipProps} from './components/chipsInput'; +export {default as ChipsInput, ChipsInputProps, ChipsInputChipProps} from './incubator/ChipsInput'; export {default as Chip, ChipProps} from './components/chip'; export {default as ColorPicker, ColorPickerProps} from './components/colorPicker'; export {default as ColorPalette, ColorPaletteProps} from './components/colorPalette'; From 092364d85caa8bbefef8a4bc1d9458b7ad069ae7 Mon Sep 17 00:00:00 2001 From: Adi Date: Wed, 15 Feb 2023 15:40:22 +0200 Subject: [PATCH 2/3] removed old ChipsInput componenet --- src/components/chipsInput/Presenter.ts | 39 -- .../chipsInput/__tests__/index.spec.js | 140 ---- src/components/chipsInput/chipsInput.api.json | 82 --- src/components/chipsInput/index.tsx | 625 ------------------ 4 files changed, 886 deletions(-) delete mode 100644 src/components/chipsInput/Presenter.ts delete mode 100644 src/components/chipsInput/__tests__/index.spec.js delete mode 100644 src/components/chipsInput/chipsInput.api.json delete mode 100644 src/components/chipsInput/index.tsx diff --git a/src/components/chipsInput/Presenter.ts b/src/components/chipsInput/Presenter.ts deleted file mode 100644 index 4c170b2aef..0000000000 --- a/src/components/chipsInput/Presenter.ts +++ /dev/null @@ -1,39 +0,0 @@ -import {Colors} from '../../style'; -import {ChipsInputChipProps, ChipsInputProps} from './index'; - -export const hasInvalidChip = (chips: Array) => { - return chips.filter((chip) => chip.invalid === true)[0] !== undefined; -}; - -export const getValidationBasedColor = (chips: Array, defaultChip?: ChipsInputChipProps) => { - const dismissColor = defaultChip?.dismissColor || Colors.red30; - - return hasInvalidChip(chips) ? dismissColor : Colors.$backgroundPrimaryHeavy; -}; - -export const getCounterTextColor = (stateChips: Array, props: ChipsInputProps) => { - const {maxLength} = props; - if (isDisabled(props)) { - return Colors.grey50; - } - return maxLength && stateChips.length >= maxLength ? Colors.red30 : Colors.grey30; -}; - -export const getCounterText = (count: number, maxLength: number) => { - return `${Math.min(count, maxLength)} / ${maxLength}`; -}; - -export const getChipDismissColor = ( - chip: ChipsInputChipProps, - isSelected: boolean, - defaultChipProps?: ChipsInputChipProps -) => { - const dismissColor = defaultChipProps?.dismissColor || Colors.white; - return !chip.invalid ? dismissColor : isSelected ? Colors.red10 : Colors.red30; -}; - -export const isDisabled = (props: ChipsInputProps) => { - const {disableTagRemoval, editable} = props; - return disableTagRemoval || editable === false; -}; - diff --git a/src/components/chipsInput/__tests__/index.spec.js b/src/components/chipsInput/__tests__/index.spec.js deleted file mode 100644 index a05e3cd900..0000000000 --- a/src/components/chipsInput/__tests__/index.spec.js +++ /dev/null @@ -1,140 +0,0 @@ -import _ from 'lodash'; -import {Constants} from '../../../commons'; -import {ChipsInput} from '../index'; - -describe('ChipsInput', () => { - let uut; - beforeEach(() => { - uut = new ChipsInput({}); - uut.setState = jest.fn(state => _.assign(uut.state, state)); - _.set(uut.state, 'chips', [{}, {}, {}]); - }); - - describe('getLabel', () => { - it('should return the string value in case item is a string', () => { - expect(uut.getLabel('value')).toBe('value'); - expect(uut.getLabel('label')).toBe('label'); - }); - - it('should return the label prop value in case item is an object and getLabel was not provided', () => { - expect(uut.getLabel({label: 'labelValue'})).toBe('labelValue'); - expect(uut.getLabel({label2: 'labelValue'})).toBe(undefined); - }); - - it('should return the label according to getLabel callback provided in props', () => { - const getLabel = jest.fn(item => item.value); - uut = new ChipsInput({getLabel}); - expect(uut.getLabel({value: 'label', label: 'bla'})).toBe('label'); - }); - - it('should return the label according to getLabel callback even if item is a string', () => { - const getLabel = jest.fn(item => `${item}1`); - uut = new ChipsInput({getLabel}); - expect(uut.getLabel('label')).toBe('label1'); - }); - }); - - describe('onKeyPress', () => { - let removeTagSpy; - beforeEach(() => { - removeTagSpy = jest.spyOn(uut, 'removeMarkedTag'); - }); - - it('should update state - tagIndexToRemove with last tag index', () => { - const pressEvent = {nativeEvent: {key: Constants.backspaceKey}}; - uut.onKeyPress(pressEvent); - expect(uut.state.chipIndexToRemove).toBe(2); - }); - - it('should not update state if keyCode is not backspace', () => { - const pressEvent = {nativeEvent: {key: 'space'}}; - uut.onKeyPress(pressEvent); - expect(uut.state.chipIndexToRemove).toBe(undefined); - expect(removeTagSpy).not.toHaveBeenCalled(); - }); - - it('should not update state if there are not tags', () => { - const pressEvent = {nativeEvent: {key: Constants.backspaceKey}}; - _.set(uut.state, 'chips', []); - uut.onKeyPress(pressEvent); - expect(uut.state.chipIndexToRemove).toBe(undefined); - expect(removeTagSpy).not.toHaveBeenCalled(); - }); - - it('should not update state if input value is not empty', () => { - const pressEvent = {nativeEvent: {key: Constants.backspaceKey}}; - _.set(uut.state, 'chips', [{}, {}, {}]); - _.set(uut.state, 'value', 'some text'); - uut.onKeyPress(pressEvent); - expect(uut.state.chipIndexToRemove).toBe(undefined); - expect(removeTagSpy).not.toHaveBeenCalled(); - }); - - it('should invoke onKeyPress callback provided in props with the event', () => { - const pressEvent = {nativeEvent: {key: 'space'}}; - const onKeyPressCallback = jest.fn(); - uut = new ChipsInput({onKeyPress: onKeyPressCallback}); - - uut.onKeyPress(pressEvent); - expect(onKeyPressCallback).toHaveBeenCalledWith(pressEvent); - }); - - it('should not set last tag index if it is already set to last index, instead call remove tag', () => { - const pressEvent = {nativeEvent: {key: Constants.backspaceKey}}; - _.set(uut.state, 'chipIndexToRemove', 2); - uut.onKeyPress(pressEvent); - expect(removeTagSpy).toHaveBeenCalled(); - expect(uut.state.chipIndexToRemove).toBe(undefined); - }); - - it('should not remove tag nor update chipIndexToRemove if pressed any key while chipIndexToRemove was set', () => { - const pressEvent = {nativeEvent: {key: 'space'}}; - _.set(uut.state, 'chipIndexToRemove', 2); - uut.onKeyPress(pressEvent); - expect(removeTagSpy).not.toHaveBeenCalled(); - expect(uut.state.chipIndexToRemove).toBe(2); - }); - }); - - describe('removeMarkedTag', () => { - const onChangeTagsCallback = jest.fn(); - beforeEach(() => { - _.set(uut, 'props.onChangeTags', onChangeTagsCallback); - }); - it('should not change tags if there is no chipIndexToRemove in state', () => { - const tags = [{}, {}]; - _.set(uut, 'state', {chipIndexToRemove: undefined, tags}); - uut.removeMarkedTag(); - expect(uut.state.tags).toEqual(tags); - expect(onChangeTagsCallback).not.toHaveBeenCalled(); - }); - - it('should remove tag according to the chipIndexToRemove in state and invoke ', () => { - const chips = [{}, {}, {}]; - const chipIndexToRemove = 2; - const removedTag = chips[chipIndexToRemove]; - _.set(uut, 'state', {chipIndexToRemove, chips}); - uut.removeMarkedTag(); - expect(uut.state.chips).toEqual([chips[0], chips[1]]); - expect(onChangeTagsCallback).toHaveBeenCalledWith([chips[0], chips[1]], 'removed', removedTag); - expect(uut.state.chipIndexToRemove).toBeUndefined(); - }); - }); - - describe('onTagPress', () => { - it('should set chipIndexToRemove according to given index', () => { - _.set(uut, 'state.chipIndexToRemove', undefined); - uut.onTagPress(1); - expect(uut.state.chipIndexToRemove).toBe(1); - uut.onTagPress(2); - expect(uut.state.chipIndexToRemove).toBe(2); - }); - - it('should call to removeMarkedTag if the given index is the same as the current chipIndexToRemove', () => { - const removeTagSpy = jest.spyOn(uut, 'removeMarkedTag'); - _.set(uut, 'state.chipIndexToRemove', 1); - uut.onTagPress(1); - expect(removeTagSpy).toHaveBeenCalledWith(); - }); - }); -}); diff --git a/src/components/chipsInput/chipsInput.api.json b/src/components/chipsInput/chipsInput.api.json deleted file mode 100644 index 34577d560b..0000000000 --- a/src/components/chipsInput/chipsInput.api.json +++ /dev/null @@ -1,82 +0,0 @@ -{ - "name": "ChipsInput", - "category": "form", - "description": "Chips input component", - "extends": ["incubator/TextField"], - "modifiers": ["typography"], - "example": "https://github.com/wix/react-native-ui-lib/blob/master/demo/src/screens/componentScreens/ChipsInputScreen.tsx", - "images": ["https://github.com/wix/react-native-ui-lib/blob/master/demo/showcase/ChipsInput/ChipsInput.gif?raw=true"], - "props": [ - { - "name": "tags", - "type": "ChipType[]", - "description": "use chips instead. List of tags. can be string boolean or custom object when implementing getLabel", - "deprecated": true - }, - { - "name": "chips", - "type": "ChipsInputChipProps[]", - "description": "List of tags. can be string boolean or custom object when implementing getLabel" - }, - {"name": "defaultChipProps", "type": "ChipsInputChipProps", "description": "Style your chips"}, - { - "name": "getLabel", - "type": "(tag: ChipType) => any", - "description": "Callback for extracting the label out of the tag item" - }, - { - "name": "renderTag", - "type": "(tag: ChipType, index: number, shouldMarkTag: boolean, label: string) => React.ReactElement", - "description": "use chips instead. Callback for custom rendering tag item", - "deprecated": true - }, - {"name": "onChangeTags", "type": "() => void", "description": "Callback for 'onChangeTags' event"}, - { - "name": "onCreateTag", - "type": "(value: any) => void", - "description": "Use chips instead. callback for creating new tag out of input value (good for composing tag object)", - "deprecated": true - }, - { - "name": "onTagPress", - "type": "(index: number, toRemove?: number) => void", - "description": "use chips instead. callback for when pressing a tag in the following format (tagIndex, markedTagIndex) => {...}", - "deprecated": true - }, - { - "name": "validationErrorMessage", - "type": "string", - "description": "Validation message error appears when tag isn't validate" - }, - {"name": "disableTagRemoval", "type": "boolean", "description": "If true, tags *removal* UX won't be available"}, - { - "name": "disableTagAdding", - "type": "boolean", - "description": "If true, tags *adding* UX (i.e. by 'submitting' the input text) won't be available" - }, - {"name": "tagStyle", "type": "ViewStyle", "description": "Custom styling for the tag item"}, - {"name": "inputStyle", "type": "RNTextInputProps['style']", "description": "Custom styling for the text input"}, - {"name": "hideUnderline", "type": "boolean", "description": "Should hide input underline"}, - {"name": "maxLength", "type": "number", "description": "Maximum numbers of chips"}, - { - "name": "scrollViewProps", - "type": "ScrollViewProps", - "description": "Chips with 'maxHeigh' is inside a ScrollView" - }, - {"name": "maxHeight", "type": "number", "description": "Chips inside a ScrollView"}, - { - "name": "leftElement", - "type": "JSX.Element | JSX.Element[]", - "description": "Custom element before the chips, for example 'search' icon, 'To:' label etc'" - }, - {"name": "value", "type": "any", "description": "The input's value"}, - {"name": "selectionColor", "type": "string | number", "description": "The color for the selection state"}, - {"name": "containerStyle", "type": "ViewStyle", "description": "Component's container style"} - ], - "snippet": [ - "" - ] -} diff --git a/src/components/chipsInput/index.tsx b/src/components/chipsInput/index.tsx deleted file mode 100644 index 68014384f4..0000000000 --- a/src/components/chipsInput/index.tsx +++ /dev/null @@ -1,625 +0,0 @@ -import _ from 'lodash'; -import React, {Component} from 'react'; -import {NativeModules, StyleSheet, ViewStyle, TextInput, NativeSyntheticEvent, TextInputKeyPressEventData, findNodeHandle, ScrollView, ScrollViewProps, TextInputProps as RNTextInputProps} from 'react-native'; -import {Colors, BorderRadiuses, ThemeManager, Typography, Spacings} from '../../style'; -import Assets from '../../assets'; -import {LogService} from '../../services'; -import {Constants, asBaseComponent, BaseComponentInjectedProps, TypographyModifiers} from '../../commons/new'; -import TextFieldMigrator from '../textField/TextFieldMigrator'; -import View from '../view'; -import TouchableOpacity from '../touchableOpacity'; -import Text from '../text'; -import Chip, {ChipProps} from '../chip'; -import Icon from '../icon'; -import {getValidationBasedColor, getCounterTextColor, getCounterText, getChipDismissColor, isDisabled} from './Presenter'; -import {TextFieldProps} from '../../../typings/components/Inputs'; - -// TODO: support updating tags externally -// TODO: support char array as tag creators (like comma) -// TODO: add notes to Docs about the Android fix for onKeyPress - -type ChipType = string | boolean | any; -export type ChipsInputChipProps = ChipProps & {invalid?: boolean} - -export type ChipsInputProps = TypographyModifiers & TextFieldProps & { - /** - * DEPRECATED: use chips instead. list of tags. can be string boolean or custom object when implementing getLabel - */ - tags?: ChipType[]; - /** - * list of tags. can be string boolean or custom object when implementing getLabel - */ - chips?: ChipsInputChipProps[]; - /** - * Style your chips - */ - defaultChipProps?: ChipsInputChipProps; - /** - * callback for extracting the label out of the tag item - */ - getLabel?: (tag: ChipType) => any; - /** - * DEPRECATED: use chips instead. callback for custom rendering tag item - */ - renderTag?: (tag: ChipType, index: number, shouldMarkTag: boolean, label: string) => React.ReactElement; - /** - * callback for onChangeTags event - */ - onChangeTags?: () => void; - /** - * DEPRECATED: use chips instead. callback for creating new tag out of input value (good for composing tag object) - */ - onCreateTag?: (value: any) => void; - /** - * DEPRECATED: use chips instead. callback for when pressing a tag in the following format (tagIndex, markedTagIndex) => {...} - */ - onTagPress?: (index: number, toRemove?: number) => void; - /** - * validation message error appears when tag isn't validate - */ - validationErrorMessage?: string; - /** - * if true, tags *removal* Ux won't be available - */ - disableTagRemoval?: boolean; - /** - * if true, tags *adding* Ux (i.e. by 'submitting' the input text) won't be available - */ - disableTagAdding?: boolean; - /** - * custom styling for the component container - */ - containerStyle?: ViewStyle; - /** - * custom styling for the tag item - */ - tagStyle?: ViewStyle; - /** - * custom styling for the text input - */ - inputStyle?: RNTextInputProps['style']; - /** - * should hide input underline - */ - hideUnderline?: boolean; - /** - * Maximum numbers of chips - */ - maxLength?: number; - /** - * Chips with maxHeigh is inside a scrollView - */ - scrollViewProps?: ScrollViewProps; - /** - * Chips inside a ScrollView - */ - maxHeight?: number; - /** - * Custom element before the chips, for example 'search' icon, 'To:' label etc' - */ - leftElement?: JSX.Element | JSX.Element[]; - - value?: any; - - selectionColor?: string | number; -} - -type State = { - value: any; - chips: Array; - chipIndexToRemove?: number; - initialChips?: Array; - isFocused: boolean; -} - -const GUTTER_SPACING = 8; - -type OwnProps = ChipsInputProps & BaseComponentInjectedProps; - -/** - * @description: Chips input component - * @modifiers: Typography - * @gif: https://github.com/wix/react-native-ui-lib/blob/master/demo/showcase/ChipsInput/ChipsInput.gif?raw=true - * @example: https://github.com/wix/react-native-ui-lib/blob/master/demo/src/screens/componentScreens/ChipsInputScreen.tsx - * @extends: TextField - */ -class ChipsInput extends Component { - static displayName = 'ChipsInput'; - - static onChangeTagsActions = { - ADDED: 'added', - REMOVED: 'removed' - }; - - input = React.createRef(); - scrollRef = React.createRef(); - - constructor(props: OwnProps) { - super(props); - - this.state = { - value: props.value, - chips: _.cloneDeep(props.tags || props.chips) || [], - chipIndexToRemove: undefined, - initialChips: props.tags || props.chips, - isFocused: this.input.current?.isFocused() || false - }; - - LogService.componentDeprecationWarn({oldComponent: 'ChipsInput', newComponent: 'Incubator.ChipsInput'}); - } - - componentDidMount() { - if (Constants.isAndroid) { - const textInputHandle = findNodeHandle(this.input.current); - if (textInputHandle && NativeModules.TextInputDelKeyHandler) { - NativeModules.TextInputDelKeyHandler.register(textInputHandle); - } - } - } - - static getDerivedStateFromProps(nextProps: Readonly, prevState: State) { - const {tags, chips} = nextProps; - if (tags && tags !== prevState.initialChips || chips && chips !== prevState.initialChips) { - return { - initialChips: nextProps.tags || nextProps.chips, - chips: nextProps.tags || nextProps.chips - }; - } - return null; - } - - addTag = () => { - const {onCreateTag, disableTagAdding, maxLength, chips: chipsProps} = this.props; - const {value, chips} = this.state; - - if (this.scrollRef?.current?.scrollToEnd) { - this.scrollRef?.current?.scrollToEnd(); - } - - if (disableTagAdding) { - return; - } - if (_.isNil(value) || _.isEmpty(value.trim())) { - return; - } - - if (maxLength && this.state.chips.length >= maxLength) { - this.setState({value: ''}); - return; - } - - const newChip = _.isFunction(onCreateTag) ? onCreateTag(value) : chipsProps ? {label: value} : value; - const newChips = [...chips, newChip]; - - this.setState({ - value: '', - chips: newChips - }); - - _.invoke(this.props, 'onChangeTags', newChips, ChipsInput.onChangeTagsActions.ADDED, newChip); - this.clear(); - } - - removeMarkedTag() { - const {chips, chipIndexToRemove} = this.state; - - if (!_.isUndefined(chipIndexToRemove)) { - const removedTag = chips[chipIndexToRemove]; - - chips.splice(chipIndexToRemove, 1); - this.setState({ - chips, - chipIndexToRemove: undefined - }); - - _.invoke(this.props, 'onChangeTags', chips, ChipsInput.onChangeTagsActions.REMOVED, removedTag); - } - } - - markTagIndex = (chipIndex: number) => { - this.setState({chipIndexToRemove: chipIndex}); - } - - onChangeText = _.debounce((value) => { - this.setState({value, chipIndexToRemove: undefined}); - _.invoke(this.props, 'onChangeText', value); - }, 0); - - onTagPress(index: number) { - const {onTagPress} = this.props; - const {chipIndexToRemove} = this.state; - - // custom press handler - if (onTagPress) { - onTagPress(index, chipIndexToRemove); - return; - } - - // default press handler - if (chipIndexToRemove === index) { - this.removeMarkedTag(); - } else { - this.markTagIndex(index); - } - } - - isLastTagMarked() { - const {chips, chipIndexToRemove} = this.state; - const tagsCount = _.size(chips); - const isLastTagMarked = chipIndexToRemove === tagsCount - 1; - - return isLastTagMarked; - } - - removeTag = () => { - const {value, chips, chipIndexToRemove} = this.state; - const tagsCount = _.size(chips); - const hasNoValue = _.isEmpty(value); - const hasTags = tagsCount > 0; - - const {disableTagRemoval} = this.props; - if (disableTagRemoval) { - return; - } - - if (hasNoValue && hasTags && _.isUndefined(chipIndexToRemove)) { - this.setState({ - chipIndexToRemove: tagsCount - 1 - }); - } else if (!_.isUndefined(chipIndexToRemove)) { - this.removeMarkedTag(); - } - } - - onKeyPress = (event: NativeSyntheticEvent) => { - _.invoke(this.props, 'onKeyPress', event); - - const keyCode = _.get(event, 'nativeEvent.key'); - const pressedBackspace = keyCode === Constants.backspaceKey; - - if (pressedBackspace) { - this.removeTag(); - } - } - - getLabel = (item: ChipType) => { - const {getLabel} = this.props; - - if (getLabel) { - return getLabel(item); - } - if (_.isString(item)) { - return item; - } - return _.get(item, 'label'); - } - - onFocus = () => { - this.setState({isFocused: true}); - } - - onBlur = () => { - this.setState({isFocused: false}); - } - - renderLabel(tag: ChipType, shouldMarkTag: boolean) { - const {typography} = this.props.modifiers; - const label = this.getLabel(tag); - - return ( - - {shouldMarkTag && ( - ) - } - - {!tag.invalid && shouldMarkTag ? 'Remove' : label} - - - ); - } - - renderTag = (tag: ChipType, index: number) => { - const {tagStyle, renderTag} = this.props; - const {chipIndexToRemove} = this.state; - const shouldMarkTag = chipIndexToRemove === index; - const markedTagStyle = tag.invalid ? styles.invalidMarkedTag : styles.tagMarked; - const defaultTagStyle = tag.invalid ? styles.invalidTag : styles.tag; - - if (_.isFunction(renderTag)) { - return renderTag(tag, index, shouldMarkTag, this.getLabel(tag)); - } - - return ( - - {this.renderLabel(tag, shouldMarkTag)} - - ); - } - - renderTagWrapper = (tag: ChipType, index: number) => { - return ( - this.onTagPress(index)} - accessibilityHint={!this.props.disableTagRemoval ? 'tap twice for remove tag mode' : undefined} - > - {this.renderTag(tag, index)} - - ); - } - - renderNewChip = () => { - const {defaultChipProps} = this.props; - const {chipIndexToRemove, chips} = this.state; - const disabled = isDisabled(this.props); - - return _.map(chips, (chip, index) => { - const selected = chipIndexToRemove === index; - const dismissColor = getChipDismissColor(chip, selected, defaultChipProps); - return ( - - this.onTagPress(index)} - onDismiss={selected ? () => this.onTagPress(index) : undefined} - dismissColor={dismissColor} - dismissIcon={Assets.icons.xSmall} - dismissIconStyle={styles.dismissIconStyle} - /> - - ); - }); - } - - renderTitleText = () => { - const {title, defaultChipProps} = this.props; - const color = this.state.isFocused ? getValidationBasedColor(this.state.chips, defaultChipProps) : Colors.grey30; - return title && ( - {title} - ); - }; - - renderChips = () => { - const {disableTagRemoval, chips: chipsProps} = this.props; - const {chips} = this.state; - const renderFunction = disableTagRemoval ? this.renderTag : this.renderTagWrapper; - - if (chipsProps) { - return this.renderNewChip(); - } else { - // The old way of creating the 'Chip' internally - return _.map(chips, (tag, index) => { - return ( - - {renderFunction(tag, index)} - - ); - }); - } - } - - renderCharCounter() { - const {maxLength} = this.props; - const counter = this.state.chips.length; - - - if (maxLength) { - const color = getCounterTextColor(this.state.chips, this.props); - const counterText = getCounterText(counter, maxLength); - - return ( - - {counterText} - - ); - } - } - - renderUnderline = () => { - const {isFocused, chips} = this.state; - const {defaultChipProps} = this.props; - const color = getValidationBasedColor(chips, defaultChipProps); - return ; - } - - renderTextInput() { - const {inputStyle, selectionColor, title, ...others} = this.props; - const {value} = this.state; - const isLastTagMarked = this.isLastTagMarked(); - - return ( - - - - ); - } - - renderChipsContainer = () => { - const {maxHeight, scrollViewProps} = this.props; - const Container = maxHeight ? ScrollView : View; - return ( - - {this.renderChips()} - {this.renderTextInput()} - - ); - } - - render() { - const {containerStyle, hideUnderline, validationErrorMessage, leftElement, maxHeight, chips} = this.props; - const {chipIndexToRemove} = this.state; - - return ( - - {!!chips && this.renderTitleText()} - - {leftElement} - {this.renderChipsContainer()} - - {!hideUnderline && this.renderUnderline()} - {this.renderCharCounter()} - {validationErrorMessage ? - ( - - - {validationErrorMessage} - - - ) : null} - - ); - } - - blur() { - this.input.current?.blur(); - } - - focus() { - this.input.current?.focus(); - } - - clear() { - this.input.current?.clear(); - } -} - -export {ChipsInput}; // For tests -export default asBaseComponent(ChipsInput); - - -const basicTagStyle = { - borderRadius: BorderRadiuses.br100, - paddingVertical: 4.5, - paddingHorizontal: 12, - marginRight: GUTTER_SPACING, - marginVertical: GUTTER_SPACING / 2 -}; - -const styles = StyleSheet.create({ - withUnderline: { - borderBottomWidth: StyleSheet.hairlineWidth, - borderColor: ThemeManager.dividerColor - }, - tagsList: { - minHeight: 38, - backgroundColor: 'transparent', - flexDirection: 'row', - flexWrap: 'wrap' - }, - tagListContainer: { - backgroundColor: 'transparent', - flexDirection: 'row', - flexWrap: 'nowrap' - }, - inputWrapper: { - flexGrow: 1, - minWidth: 120, - backgroundColor: 'transparent', - justifyContent: 'center' - }, - tag: { - borderWidth: 0, - paddingVertical: 5, - backgroundColor: Colors.$backgroundPrimaryHeavy - }, - invalidTag: { - borderWidth: 1, - borderColor: Colors.red30, - backgroundColor: 'transparent' - }, - basicTagStyle: { - ...basicTagStyle - }, - invalidMarkedTag: { - borderColor: Colors.red10 - }, - tagMarked: { - backgroundColor: Colors.grey10 - }, - dismissIconStyle: { - width: 10, - height: 10, - marginRight: Spacings.s1 - }, - removeIcon: { - tintColor: Colors.white, - width: 10, - height: 10, - marginRight: 6 - }, - invalidTagRemoveIcon: { - tintColor: Colors.red10 - }, - tagLabel: { - ...Typography.text80, - color: Colors.white - }, - errorMessage: { - ...Typography.text80, - color: Colors.red30 - }, - errorMessageWhileMarked: { - color: Colors.red10 - }, - label: { - marginTop: Spacings.s1, - alignSelf: 'flex-end', - height: Typography.text80?.lineHeight, - ...Typography.text80 - }, - alignTextCenter: { - textAlignVertical: 'center' - } -}); From d6cc14cdf7543815e7a873d92608d3c0758a8b0b Mon Sep 17 00:00:00 2001 From: Adi Date: Wed, 15 Feb 2023 15:58:24 +0200 Subject: [PATCH 3/3] Moved Incubator ChipsInput to components, fixed review notes --- demo/src/index.js | 3 +++ demo/src/screens/MenuStructure.js | 2 +- .../ChipsInputScreen.tsx} | 6 +++--- demo/src/screens/componentScreens/index.js | 1 + demo/src/screens/incubatorScreens/index.js | 1 - .../chipsInput}/assets/xSmall.png | Bin .../chipsInput}/assets/xSmall@1.5x.png | Bin .../chipsInput}/assets/xSmall@2x.png | Bin .../chipsInput}/assets/xSmall@3x.png | Bin .../chipsInput}/assets/xSmall@4x.png | Bin .../chipsInput}/chipsInput.api.json | 0 .../ChipsInput => components/chipsInput}/index.tsx | 4 ++-- src/incubator/index.ts | 2 +- src/index.ts | 2 +- 14 files changed, 12 insertions(+), 9 deletions(-) rename demo/src/screens/{incubatorScreens/IncubatorChipsInputScreen.tsx => componentScreens/ChipsInputScreen.tsx} (90%) rename src/{incubator/ChipsInput => components/chipsInput}/assets/xSmall.png (100%) rename src/{incubator/ChipsInput => components/chipsInput}/assets/xSmall@1.5x.png (100%) rename src/{incubator/ChipsInput => components/chipsInput}/assets/xSmall@2x.png (100%) rename src/{incubator/ChipsInput => components/chipsInput}/assets/xSmall@3x.png (100%) rename src/{incubator/ChipsInput => components/chipsInput}/assets/xSmall@4x.png (100%) rename src/{incubator/ChipsInput => components/chipsInput}/chipsInput.api.json (100%) rename src/{incubator/ChipsInput => components/chipsInput}/index.tsx (97%) diff --git a/demo/src/index.js b/demo/src/index.js index 9a6b06c717..2879e7d3bd 100644 --- a/demo/src/index.js +++ b/demo/src/index.js @@ -57,6 +57,9 @@ module.exports = { get ExpandableSectionScreen() { return require('./screens/componentScreens/ExpandableSectionScreen').default; }, + get ChipsInputScreen() { + return require('./screens/componentScreens/ChipsInputScreen').default; + }, get HapticScreen() { return require('./screens/componentScreens/HapticScreen').default; }, diff --git a/demo/src/screens/MenuStructure.js b/demo/src/screens/MenuStructure.js index 2e8cc3f843..592691871e 100644 --- a/demo/src/screens/MenuStructure.js +++ b/demo/src/screens/MenuStructure.js @@ -174,7 +174,7 @@ export const navigationData = { title: 'Incubator (Experimental)', screens: [ {title: 'Calendar', tags: 'calendar', screen: 'unicorn.components.IncubatorCalendarScreen'}, - {title: 'ChipsInput', tags: 'chips input', screen: 'unicorn.components.IncubatorChipsInputScreen'}, + {title: 'ChipsInput', tags: 'chips input', screen: 'unicorn.components.ChipsInputScreen'}, {title: 'Native TouchableOpacity', tags: 'touchable native', screen: 'unicorn.incubator.TouchableOpacityScreen'}, {title: 'Dialog (New)', tags: 'dialog modal popup alert', screen: 'unicorn.incubator.IncubatorDialogScreen'}, {title: 'TextField (New)', tags: 'text field input', screen: 'unicorn.components.IncubatorTextFieldScreen'}, diff --git a/demo/src/screens/incubatorScreens/IncubatorChipsInputScreen.tsx b/demo/src/screens/componentScreens/ChipsInputScreen.tsx similarity index 90% rename from demo/src/screens/incubatorScreens/IncubatorChipsInputScreen.tsx rename to demo/src/screens/componentScreens/ChipsInputScreen.tsx index d615002dab..8b5ff6673c 100644 --- a/demo/src/screens/incubatorScreens/IncubatorChipsInputScreen.tsx +++ b/demo/src/screens/componentScreens/ChipsInputScreen.tsx @@ -1,5 +1,5 @@ import React, {Component} from 'react'; -import {View, Text, Card, TextField, Button, Colors, Incubator} from 'react-native-ui-lib'; //eslint-disable-line +import {View, Text, Card, TextField, Button, Colors, ChipsInput} from 'react-native-ui-lib'; //eslint-disable-line import _ from 'lodash'; export default class ChipsInputScreen extends Component { @@ -14,7 +14,7 @@ export default class ChipsInputScreen extends Component { ChipsInput - - require('./CarouselVerticalScreen').default); registrar('unicorn.components.CheckboxScreen', () => require('./CheckboxScreen').default); registrar('unicorn.components.ChipScreen', () => require('./ChipScreen').default); + registrar('unicorn.components.ChipsInputScreen', () => require('./ChipsInputScreen').default); registrar('unicorn.components.ColorPickerScreen', () => require('./ColorPickerScreen').default); registrar('unicorn.components.ColorSwatchScreen', () => require('./ColorSwatchScreen').default); registrar('unicorn.components.ConnectionStatusBar', () => require('./ConnectionStatusBarScreen').default); diff --git a/demo/src/screens/incubatorScreens/index.js b/demo/src/screens/incubatorScreens/index.js index 79d39e0c92..0159bc777b 100644 --- a/demo/src/screens/incubatorScreens/index.js +++ b/demo/src/screens/incubatorScreens/index.js @@ -2,7 +2,6 @@ import {gestureHandlerRootHOC} from 'react-native-gesture-handler'; export function registerScreens(registrar) { registrar('unicorn.components.IncubatorCalendarScreen', () => require('./IncubatorCalendarScreen').default); - registrar('unicorn.components.IncubatorChipsInputScreen', () => require('./IncubatorChipsInputScreen').default); registrar('unicorn.incubator.TouchableOpacityScreen', () => gestureHandlerRootHOC(require('./TouchableOpacityScreen').default)); registrar('unicorn.incubator.IncubatorDialogScreen', () => require('./IncubatorDialogScreen').default); diff --git a/src/incubator/ChipsInput/assets/xSmall.png b/src/components/chipsInput/assets/xSmall.png similarity index 100% rename from src/incubator/ChipsInput/assets/xSmall.png rename to src/components/chipsInput/assets/xSmall.png diff --git a/src/incubator/ChipsInput/assets/xSmall@1.5x.png b/src/components/chipsInput/assets/xSmall@1.5x.png similarity index 100% rename from src/incubator/ChipsInput/assets/xSmall@1.5x.png rename to src/components/chipsInput/assets/xSmall@1.5x.png diff --git a/src/incubator/ChipsInput/assets/xSmall@2x.png b/src/components/chipsInput/assets/xSmall@2x.png similarity index 100% rename from src/incubator/ChipsInput/assets/xSmall@2x.png rename to src/components/chipsInput/assets/xSmall@2x.png diff --git a/src/incubator/ChipsInput/assets/xSmall@3x.png b/src/components/chipsInput/assets/xSmall@3x.png similarity index 100% rename from src/incubator/ChipsInput/assets/xSmall@3x.png rename to src/components/chipsInput/assets/xSmall@3x.png diff --git a/src/incubator/ChipsInput/assets/xSmall@4x.png b/src/components/chipsInput/assets/xSmall@4x.png similarity index 100% rename from src/incubator/ChipsInput/assets/xSmall@4x.png rename to src/components/chipsInput/assets/xSmall@4x.png diff --git a/src/incubator/ChipsInput/chipsInput.api.json b/src/components/chipsInput/chipsInput.api.json similarity index 100% rename from src/incubator/ChipsInput/chipsInput.api.json rename to src/components/chipsInput/chipsInput.api.json diff --git a/src/incubator/ChipsInput/index.tsx b/src/components/chipsInput/index.tsx similarity index 97% rename from src/incubator/ChipsInput/index.tsx rename to src/components/chipsInput/index.tsx index 99e928fb1f..aeef541a00 100644 --- a/src/incubator/ChipsInput/index.tsx +++ b/src/components/chipsInput/index.tsx @@ -3,8 +3,8 @@ import {StyleSheet, NativeSyntheticEvent, TextInputKeyPressEventData} from 'reac import {isUndefined, map} from 'lodash'; import {Constants} from '../../commons/new'; import {useCombinedRefs} from '../../hooks'; -import TextField, {TextFieldProps} from '../TextField'; -import Chip, {ChipProps} from '../../components/chip'; +import TextField, {TextFieldProps} from '../../incubator/TextField'; +import Chip, {ChipProps} from '../chip'; const removeIcon = require('./assets/xSmall.png'); diff --git a/src/incubator/index.ts b/src/incubator/index.ts index 5da84aa4ed..eba6b095c8 100644 --- a/src/incubator/index.ts +++ b/src/incubator/index.ts @@ -1,5 +1,5 @@ // export {default as Calendar} from './Calendar'; -export {default as ChipsInput, ChipsInputProps, ChipsInputChangeReason, ChipsInputChipProps} from './ChipsInput'; +export {default as ChipsInput, ChipsInputProps, ChipsInputChangeReason, ChipsInputChipProps} from '../components/chipsInput'; export {default as ExpandableOverlay} from './expandableOverlay'; // @ts-ignore export {default as TextField, TextFieldProps, FieldContextType} from './TextField'; diff --git a/src/index.ts b/src/index.ts index 52ac56cf20..634fd2982d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -57,7 +57,7 @@ export {default as Button, ButtonProps, ButtonSize, ButtonAnimationDirection} fr export {default as Card, CardProps, CardSectionProps, CardSelectionOptions} from './components/card'; export {default as Carousel, CarouselProps, PageControlPosition} from './components/carousel'; export {default as Checkbox, CheckboxProps} from './components/checkbox'; -export {default as ChipsInput, ChipsInputProps, ChipsInputChipProps} from './incubator/ChipsInput'; +export {default as ChipsInput, ChipsInputProps, ChipsInputChipProps} from './components/chipsInput'; export {default as Chip, ChipProps} from './components/chip'; export {default as ColorPicker, ColorPickerProps} from './components/colorPicker'; export {default as ColorPalette, ColorPaletteProps} from './components/colorPalette';