Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 23 additions & 6 deletions src/components/TextPicker/TextSelectorModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import TextInput from '@components/TextInput';
import type {BaseTextInputRef} from '@components/TextInput/BaseTextInput/types';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import {getFieldRequiredErrors} from '@libs/ValidationUtils';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {TextSelectorModalProps} from './types';
Expand All @@ -26,6 +27,7 @@ function TextSelectorModal({
onClose,
shouldClearOnClose,
maxLength = CONST.CATEGORY_NAME_LIMIT,
required = false,
...rest
}: TextSelectorModalProps) {
const {translate} = useLocalize();
Expand All @@ -50,16 +52,20 @@ function TextSelectorModal({

const validate = useCallback(
(values: FormOnyxValues<typeof ONYXKEYS.FORMS.TEXT_PICKER_MODAL_FORM>) => {
const errors: FormInputErrors<typeof ONYXKEYS.FORMS.TEXT_PICKER_MODAL_FORM> = {};
let errors: FormInputErrors<typeof ONYXKEYS.FORMS.TEXT_PICKER_MODAL_FORM> = {};
const formValue = values[rest.inputID];

if (required) {
errors = getFieldRequiredErrors(values, [rest.inputID]);
}

if (formValue.length > maxLength) {
errors[rest.inputID] = translate('common.error.characterLimitExceedCounter', {length: formValue.length, limit: maxLength});
}

return errors;
},
[maxLength, rest.inputID, translate],
[maxLength, rest.inputID, required, translate],
);

// In TextPicker, when the modal is hidden, it is not completely unmounted, so when it is shown again, the currentValue is not updated with the value prop.
Expand Down Expand Up @@ -93,6 +99,20 @@ function TextSelectorModal({
}, [isVisible]),
);

const handleSubmit = useCallback(
(data: FormOnyxValues<typeof ONYXKEYS.FORMS.TEXT_PICKER_MODAL_FORM>) => {
const submittedValue = data[rest.inputID] ?? '';

if (required && !submittedValue.trim()) {
return;
}

Keyboard.dismiss();
onValueSelected?.(submittedValue);
},
[onValueSelected, rest.inputID, required],
);

return (
<Modal
type={CONST.MODAL.MODAL_TYPE.RIGHT_DOCKED}
Expand All @@ -117,10 +137,7 @@ function TextSelectorModal({
<FormProvider
formID={ONYXKEYS.FORMS.TEXT_PICKER_MODAL_FORM}
validate={validate}
onSubmit={(data) => {
Keyboard.dismiss();
onValueSelected?.(data[rest.inputID] ?? '');
}}
onSubmit={handleSubmit}
submitButtonText={translate('common.save')}
style={[styles.mh5, styles.flex1]}
enabledWhenOffline
Expand Down
3 changes: 2 additions & 1 deletion src/components/TextPicker/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import TextSelectorModal from './TextSelectorModal';
import type {TextPickerProps} from './types';

function TextPicker(
{value, description, placeholder = '', errorText = '', onInputChange, furtherDetails, rightLabel, disabled = false, interactive = true, ...rest}: TextPickerProps,
{value, description, placeholder = '', errorText = '', onInputChange, furtherDetails, rightLabel, disabled = false, interactive = true, required = false, ...rest}: TextPickerProps,
forwardedRef: ForwardedRef<View>,
) {
const styles = useThemeStyles();
Expand Down Expand Up @@ -54,6 +54,7 @@ function TextPicker(
onClose={hidePickerModal}
onValueSelected={updateInput}
disabled={disabled}
required={required}
// eslint-disable-next-line react/jsx-props-no-spreading
{...rest}
/>
Expand Down
6 changes: 6 additions & 0 deletions src/components/TextPicker/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ type TextSelectorModalProps = {

/** The ID used to uniquely identify the input in a Form */
inputID: string;

/** Whether the field is required */
required?: boolean;
} & Pick<MenuItemBaseProps, 'subtitle' | 'description'> &
TextProps;

Expand All @@ -48,6 +51,9 @@ type TextPickerProps = {

/** The ID used to uniquely identify the input in a Form */
inputID: string;

/** Whether the field is required */
required?: boolean;
} & Pick<MenuItemBaseProps, 'rightLabel' | 'subtitle' | 'description' | 'interactive'> &
TextProps;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ function WorkspaceCreateReportFieldsPage({
maxLength={CONST.WORKSPACE_REPORT_FIELD_POLICY_MAX_LENGTH}
multiline={false}
role={CONST.ROLE.PRESENTATION}
required
/>
<InputWrapper
InputComponent={TypeSelector}
Expand Down
5 changes: 3 additions & 2 deletions src/pages/workspace/taxes/WorkspaceCreateTaxPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {createPolicyTax, getNextTaxCode, getTaxValueWithPercentage, validateTaxN
import Navigation from '@libs/Navigation/Navigation';
import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types';
import type {SettingsNavigatorParamList} from '@libs/Navigation/types';
import * as PolicyUtils from '@libs/PolicyUtils';
import {hasAccountingConnections} from '@libs/PolicyUtils';
import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper';
import type {WithPolicyAndFullscreenLoadingProps} from '@pages/workspace/withPolicyAndFullscreenLoading';
import withPolicyAndFullscreenLoading from '@pages/workspace/withPolicyAndFullscreenLoading';
Expand Down Expand Up @@ -76,7 +76,7 @@ function WorkspaceCreateTaxPage({
style={[styles.defaultModalContainer]}
>
<FullPageNotFoundView
shouldShow={PolicyUtils.hasAccountingConnections(policy)}
shouldShow={hasAccountingConnections(policy)}
addBottomSafeAreaPadding
>
<View style={[styles.h100, styles.flex1, styles.justifyContentBetween]}>
Expand All @@ -103,6 +103,7 @@ function WorkspaceCreateTaxPage({
maxLength={CONST.TAX_RATES.NAME_MAX_LENGTH}
multiline={false}
role={CONST.ROLE.PRESENTATION}
required
/>
<InputWrapper
InputComponent={AmountPicker}
Expand Down