diff --git a/packages/desktop-client/package.json b/packages/desktop-client/package.json index 79c5020dcad8fb05359c9fd0d808029b13f7904c..bfcd44fa628a5f9727706aa3dc8301e9df4f0d0a 100644 --- a/packages/desktop-client/package.json +++ b/packages/desktop-client/package.json @@ -30,7 +30,6 @@ "debounce": "^1.2.0", "downshift": "7.6.0", "focus-visible": "^4.1.1", - "formik": "^0.11.10", "glamor": "^2.20.40", "hotkeys-js": "3.10.3", "inter-ui": "^3.19.3", diff --git a/packages/desktop-client/src/components/common.tsx b/packages/desktop-client/src/components/common.tsx index e7c4fe5a845e13fe95e0b0a681b3ab197ca2fdb2..63af5c81be83a36d4feca963ae05a71dbb4b99e8 100644 --- a/packages/desktop-client/src/components/common.tsx +++ b/packages/desktop-client/src/components/common.tsx @@ -129,11 +129,11 @@ type LinkProps = ComponentProps<typeof Button>; export function LinkButton({ style, children, ...nativeProps }: LinkProps) { return ( <Button - {...css( + style={[ { textDecoration: 'none', color: styles.textColor, - backgroundColor: 'transparent !important', + backgroundColor: 'transparent', display: 'inline', border: 0, cursor: 'pointer', @@ -149,7 +149,7 @@ export function LinkButton({ style, children, ...nativeProps }: LinkProps) { }, styles.smallText, style, - )} + ]} {...nativeProps} > {children} diff --git a/packages/desktop-client/src/components/modals/CloseAccount.js b/packages/desktop-client/src/components/modals/CloseAccount.js index 9e0356461a413c2a53828ce2f55ff49c1173a893..b6e5ddddf63749c7809f6e54769d9eaf4a9cb757 100644 --- a/packages/desktop-client/src/components/modals/CloseAccount.js +++ b/packages/desktop-client/src/components/modals/CloseAccount.js @@ -1,7 +1,5 @@ import React, { useState } from 'react'; -import { Formik } from 'formik'; - import { integerToCurrency } from 'loot-core/src/shared/util'; import { colors } from '../../style'; @@ -52,6 +50,11 @@ function CloseAccount({ modalProps, }) { let [loading, setLoading] = useState(false); + let [transfer, setTransfer] = useState(''); + let [category, setCategory] = useState(''); + + let [transferError, setTransferError] = useState(false); + let [categoryError, setCategoryError] = useState(false); let filtered = accounts.filter(a => a.id !== account.id); let onbudget = filtered.filter(a => a.offbudget === 0); @@ -79,153 +82,138 @@ function CloseAccount({ </span> )} </P> - <Formik - validateOnChange={false} - initialValues={{ transfer: '', category: '' }} - onSubmit={(values, { setErrors }) => { - const errors = {}; - if (balance !== 0 && values.transfer === '') { - errors.transfer = 'required'; - } - if ( - needsCategory(account, values.transfer, accounts) && - values.category === '' - ) { - errors.category = 'required'; - } - setErrors(errors); + <form + onSubmit={event => { + event.preventDefault(); + + let transferError = balance !== 0 && transfer === ''; + setTransferError(transferError); + + let categoryError = + needsCategory(account, transfer, accounts) && category === ''; + setCategoryError(categoryError); - if (Object.keys(errors).length === 0) { + if (!transferError && !categoryError) { setLoading(true); actions - .closeAccount( - account.id, - values.transfer || null, - values.category || null, - ) + .closeAccount(account.id, transfer || null, category || null) .then(() => { modalProps.onClose(); }); } }} - render={({ - values, - errors, - touched, - handleChange, - handleBlur, - handleSubmit, - isSubmitting, - setFieldValue, - }) => ( - <form onSubmit={handleSubmit}> - {balance !== 0 && ( - <View> + > + {balance !== 0 && ( + <View> + <P> + This account has a balance of{' '} + <strong>{integerToCurrency(balance)}</strong>. To close this + account, select a different account to transfer this balance + to: + </P> + + <Select + value={transfer} + onChange={event => { + setTransfer(event.target.value); + if (transferError && event.target.value) { + setTransferError(false); + } + }} + style={{ width: 200, marginBottom: 15 }} + > + <option value="">Select account...</option> + <optgroup label="For Budget"> + {onbudget.map(acct => ( + <option key={acct.id} value={acct.id}> + {acct.name} + </option> + ))} + </optgroup> + + <optgroup label="Off Budget"> + {offbudget.map(acct => ( + <option key={acct.id} value={acct.id}> + {acct.name} + </option> + ))} + </optgroup> + </Select> + {transferError && ( + <FormError style={{ marginBottom: 15 }}> + Transfer is required + </FormError> + )} + + {needsCategory(account, transfer, accounts) && ( + <View style={{ marginBottom: 15 }}> <P> - This account has a balance of{' '} - <strong>{integerToCurrency(balance)}</strong>. To close - this account, select a different account to transfer this - balance to: + Since you are transferring the balance from a budgeted + account to an off-budget account, this transaction must be + categorized. Select a category: </P> - <Select - name="transfer" - value={values.transfer} - onChange={handleChange} - onBlur={handleBlur} - style={{ width: 200, marginBottom: 15 }} - > - <option value="">Select account...</option> - <optgroup label="For Budget"> - {onbudget.map(acct => ( - <option key={acct.id} value={acct.id}> - {acct.name} - </option> - ))} - </optgroup> - - <optgroup label="Off Budget"> - {offbudget.map(acct => ( - <option key={acct.id} value={acct.id}> - {acct.name} - </option> - ))} - </optgroup> - </Select> - {errors.transfer && ( - <FormError style={{ marginBottom: 15 }}> - Transfer is required - </FormError> + <CategorySelect + categoryGroups={categoryGroups} + value={category} + onChange={event => { + setCategory(event.target.value); + if (categoryError && event.target.value) { + setCategoryError(false); + } + }} + style={{ width: 200 }} + /> + {categoryError && ( + <FormError>Category is required</FormError> )} - - {needsCategory(account, values.transfer, accounts) && ( - <View style={{ marginBottom: 15 }}> - <P> - Since you are transferring the balance from a budgeted - account to an off-budget account, this transaction - must be categorized. Select a category: - </P> - - <CategorySelect - categoryGroups={categoryGroups} - name="category" - value={values.category} - onChange={handleChange} - onBlur={handleBlur} - style={{ width: 200 }} - /> - {errors.category && ( - <FormError>Category is required</FormError> - )} - </View> - )} - </View> - )} - - {!canDelete && ( - <View style={{ marginBottom: 15 }}> - <Text style={{ fontSize: 12 }}> - You can also{' '} - <LinkButton - onClick={() => { - setLoading(true); - - actions - .forceCloseAccount(account.id) - .then(() => modalProps.onClose()); - }} - style={{ color: colors.r6 }} - > - force close - </LinkButton>{' '} - the account which will delete it and all its transactions - permanently. Doing so may change your budget unexpectedly - since money in it may vanish. - </Text> </View> )} + </View> + )} - <View - style={{ - flexDirection: 'row', - justifyContent: 'flex-end', - }} - > - <Button - type="submit" - style={{ marginRight: 10 }} - onClick={modalProps.onClose} + {!canDelete && ( + <View style={{ marginBottom: 15 }}> + <Text style={{ fontSize: 12 }}> + You can also{' '} + <LinkButton + onClick={() => { + setLoading(true); + + actions + .forceCloseAccount(account.id) + .then(() => modalProps.onClose()); + }} + style={{ color: colors.r6 }} > - Cancel - </Button> - <Button type="submit" primary> - Close Account - </Button> - </View> - </form> + force close + </LinkButton>{' '} + the account which will delete it and all its transactions + permanently. Doing so may change your budget unexpectedly + since money in it may vanish. + </Text> + </View> )} - /> + + <View + style={{ + flexDirection: 'row', + justifyContent: 'flex-end', + }} + > + <Button + type="submit" + style={{ marginRight: 10 }} + onClick={modalProps.onClose} + > + Cancel + </Button> + <Button type="submit" primary> + Close Account + </Button> + </View> + </form> </View> )} </Modal> diff --git a/packages/desktop-client/src/components/modals/CreateLocalAccount.js b/packages/desktop-client/src/components/modals/CreateLocalAccount.js index 449e7435d72dbf8bea37f4177d9c13f1084623df..b4dd83a4a441bcf90987d66d88f490c6220df61b 100644 --- a/packages/desktop-client/src/components/modals/CreateLocalAccount.js +++ b/packages/desktop-client/src/components/modals/CreateLocalAccount.js @@ -1,8 +1,6 @@ -import React from 'react'; +import React, { useState } from 'react'; import { useNavigate } from 'react-router-dom'; -import { Formik } from 'formik'; - import { toRelaxedNumber } from 'loot-core/src/shared/util'; import { colors } from '../../style'; @@ -21,140 +19,142 @@ import { function CreateLocalAccount({ modalProps, actions }) { let navigate = useNavigate(); + let [name, setName] = useState(''); + let [offbudget, setOffbudget] = useState(false); + let [balance, setBalance] = useState('0'); + + let [nameError, setNameError] = useState(false); + let [balanceError, setBalanceError] = useState(false); + + let validateBalance = balance => !isNaN(parseFloat(balance)); + return ( <Modal title="Create Local Account" {...modalProps} showBack={false}> {() => ( <View> - <Formik - validateOnChange={false} - initialValues={{ name: '', balance: '0' }} - validate={() => ({})} - onSubmit={async (values, { setErrors }) => { - const errors = {}; - if (!values.name) { - errors.name = 'required'; - } - if (isNaN(parseFloat(values.balance))) { - errors.balance = 'format'; - } - setErrors(errors); + <form + onSubmit={async event => { + event.preventDefault(); + + let nameError = !name; + setNameError(nameError); - if (Object.keys(errors).length === 0) { + let balanceError = !validateBalance(balance); + setBalanceError(balanceError); + + if (!nameError && !balanceError) { modalProps.onClose(); let id = await actions.createAccount( - values.name, - toRelaxedNumber(values.balance), - values.offbudget, + name, + toRelaxedNumber(balance), + offbudget, ); navigate('/accounts/' + id); } }} - render={({ - values, - errors, - touched, - handleChange, - handleBlur, - handleSubmit, - isSubmitting, - setFieldValue, - }) => ( - <form onSubmit={handleSubmit}> - <InlineField label="Name" width="75%"> - <InitialFocus> - <Input - name="name" - value={values.name} - onChange={handleChange} - onBlur={handleBlur} - style={{ flex: 1 }} - /> - </InitialFocus> - </InlineField> - {errors.name && ( - <FormError style={{ marginLeft: 75 }}> - Name is required - </FormError> - )} + > + <InlineField label="Name" width="75%"> + <InitialFocus> + <Input + name="name" + value={name} + onChange={event => setName(event.target.value)} + onBlur={event => { + let name = event.target.value.trim(); + setName(name); + if (name && nameError) { + setNameError(false); + } + }} + style={{ flex: 1 }} + /> + </InitialFocus> + </InlineField> + {nameError && ( + <FormError style={{ marginLeft: 75 }}>Name is required</FormError> + )} - <View + <View + style={{ + width: '75%', + flexDirection: 'row', + justifyContent: 'flex-end', + }} + > + <View style={{ flexDirection: 'column' }}> + <label style={{ - width: '75%', - flexDirection: 'row', + userSelect: 'none', + textAlign: 'right', + width: '100%', + display: 'flex', + verticalAlign: 'center', justifyContent: 'flex-end', }} + htmlFor="offbudget" > - <View style={{ flexDirection: 'column' }}> - <label - style={{ - userSelect: 'none', - textAlign: 'right', - width: '100%', - display: 'flex', - verticalAlign: 'center', - justifyContent: 'flex-end', - }} - htmlFor="offbudget" - > - <input - id="offbudget" - name="offbudget" - type="checkbox" - checked={!!values.offbudget} - onChange={handleChange} - onBlur={handleBlur} - /> - Off-budget - </label> - <div - style={{ - textAlign: 'right', - fontSize: '0.7em', - color: colors.n5, - marginTop: 3, - }} - > - <Text> - This cannot be changed later. <br /> {'\n'} - See{' '} - <ExternalLink - linkColor="muted" - to="https://actualbudget.org/docs/accounts/#off-budget-accounts" - > - Accounts Overview - </ExternalLink>{' '} - for more information. - </Text> - </div> - </View> - </View> - - <InlineField label="Balance" width="75%"> - <Input - name="balance" - value={values.balance} - onChange={handleChange} - onBlur={handleBlur} - style={{ flex: 1 }} + <input + id="offbudget" + name="offbudget" + type="checkbox" + checked={offbudget} + onChange={event => setOffbudget(event.target.checked)} /> - </InlineField> - {errors.balance && ( - <FormError style={{ marginLeft: 75 }}> - Balance must be a number - </FormError> - )} + Off-budget + </label> + <div + style={{ + textAlign: 'right', + fontSize: '0.7em', + color: colors.n5, + marginTop: 3, + }} + > + <Text> + This cannot be changed later. <br /> {'\n'} + See{' '} + <ExternalLink + linkColor="muted" + to="https://actualbudget.org/docs/accounts/#off-budget-accounts" + > + Accounts Overview + </ExternalLink>{' '} + for more information. + </Text> + </div> + </View> + </View> - <ModalButtons> - <Button onClick={() => modalProps.onBack()} type="button"> - Back - </Button> - <Button primary style={{ marginLeft: 10 }}> - Create - </Button> - </ModalButtons> - </form> + <InlineField label="Balance" width="75%"> + <Input + name="balance" + value={balance} + onChange={event => setBalance(event.target.value)} + onBlur={event => { + let balance = event.target.value.trim(); + setBalance(balance); + if (validateBalance(balance) && balanceError) { + setBalanceError(false); + } + }} + style={{ flex: 1 }} + /> + </InlineField> + {balanceError && ( + <FormError style={{ marginLeft: 75 }}> + Balance must be a number + </FormError> )} - /> + + <ModalButtons> + <Button onClick={() => modalProps.onBack()} type="button"> + Back + </Button> + <Button primary style={{ marginLeft: 10 }}> + Create + </Button> + </ModalButtons> + </form> </View> )} </Modal> diff --git a/upcoming-release-notes/1212.md b/upcoming-release-notes/1212.md new file mode 100644 index 0000000000000000000000000000000000000000..25c41c422100913c0bf92656f5e7d50157302212 --- /dev/null +++ b/upcoming-release-notes/1212.md @@ -0,0 +1,6 @@ +--- +category: Maintenance +authors: [j-f1] +--- + +Remove usage of Formik diff --git a/yarn.lock b/yarn.lock index 139d497eeeb349a9565f91562abb819242bc1e83..bbff0d89b2f66dd884562512a27a3b3f008b9d1d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -98,7 +98,6 @@ __metadata: debounce: ^1.2.0 downshift: 7.6.0 focus-visible: ^4.1.1 - formik: ^0.11.10 glamor: ^2.20.40 hotkeys-js: 3.10.3 inter-ui: ^3.19.3 @@ -9410,21 +9409,6 @@ __metadata: languageName: node linkType: hard -"formik@npm:^0.11.10": - version: 0.11.11 - resolution: "formik@npm:0.11.11" - dependencies: - lodash.clonedeep: ^4.5.0 - lodash.isequal: 4.5.0 - lodash.topath: 4.5.2 - prop-types: ^15.5.10 - warning: ^3.0.0 - peerDependencies: - react: ">=15" - checksum: ce0fde6f1d87fa60afe3a025904e64b6394dccb48dd0739cd1d3aa987b00bad2e15584b03db7ed284b9260748db14fb36459f4768e7b93548e091223c406a0b8 - languageName: node - linkType: hard - "forwarded@npm:0.2.0": version: 0.2.0 resolution: "forwarded@npm:0.2.0" @@ -12257,13 +12241,6 @@ __metadata: languageName: node linkType: hard -"lodash.clonedeep@npm:^4.5.0": - version: 4.5.0 - resolution: "lodash.clonedeep@npm:4.5.0" - checksum: 92c46f094b064e876a23c97f57f81fbffd5d760bf2d8a1c61d85db6d1e488c66b0384c943abee4f6af7debf5ad4e4282e74ff83177c9e63d8ff081a4837c3489 - languageName: node - linkType: hard - "lodash.debounce@npm:^4.0.8": version: 4.0.8 resolution: "lodash.debounce@npm:4.0.8" @@ -12278,7 +12255,7 @@ __metadata: languageName: node linkType: hard -"lodash.isequal@npm:4.5.0, lodash.isequal@npm:^4.5.0": +"lodash.isequal@npm:^4.5.0": version: 4.5.0 resolution: "lodash.isequal@npm:4.5.0" checksum: da27515dc5230eb1140ba65ff8de3613649620e8656b19a6270afe4866b7bd461d9ba2ac8a48dcc57f7adac4ee80e1de9f965d89d4d81a0ad52bb3eec2609644 @@ -12306,13 +12283,6 @@ __metadata: languageName: node linkType: hard -"lodash.topath@npm:4.5.2": - version: 4.5.2 - resolution: "lodash.topath@npm:4.5.2" - checksum: 04583e220f4bb1c4ac0008ff8f46d9cb4ddce0ea1090085790da30a41f4cb1b904d885cb73257fca619fa825cd96f9bb97c67d039635cb76056e18f5e08bfdee - languageName: node - linkType: hard - "lodash.uniq@npm:^4.5.0": version: 4.5.0 resolution: "lodash.uniq@npm:4.5.0" @@ -18573,15 +18543,6 @@ __metadata: languageName: node linkType: hard -"warning@npm:^3.0.0": - version: 3.0.0 - resolution: "warning@npm:3.0.0" - dependencies: - loose-envify: ^1.0.0 - checksum: c9f99a12803aab81b29858e7dc3415bf98b41baee3a4c3acdeb680d98c47b6e17490f1087dccc54432deed5711a5ce0ebcda2b27e9b5eb054c32ae50acb4419c - languageName: node - linkType: hard - "warning@npm:^4.0.3": version: 4.0.3 resolution: "warning@npm:4.0.3"