Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fix] Localize currency symbol #8299

Merged
merged 32 commits into from
Jun 6, 2022
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
7890252
fix: localize currency symbol
mdneyazahmad Mar 23, 2022
3fa9bc3
refactor: move common function to hoc component
mdneyazahmad Mar 28, 2022
fad813c
refactor: rename variable names
mdneyazahmad Mar 28, 2022
0d56d96
refactor: ltr symbol component
mdneyazahmad Mar 28, 2022
1f50122
feat: add currency symbol utils
mdneyazahmad Mar 31, 2022
df003d6
refactor: currency utils functions
mdneyazahmad Mar 31, 2022
3cd30fa
test: add CurrencySymbolUtilsTest
mdneyazahmad Apr 1, 2022
155dc02
test: replace require to import
mdneyazahmad Apr 1, 2022
3e57e26
test: fix lint error
mdneyazahmad Apr 1, 2022
6a0e400
test: fix lint error
mdneyazahmad Apr 1, 2022
84bcdeb
refactor: CurrencySymbolUtils
mdneyazahmad Apr 1, 2022
696b7d5
test: refactor names
mdneyazahmad Apr 1, 2022
3ef60e8
docs: update comments
mdneyazahmad Apr 1, 2022
5992dfc
docs: update comment
mdneyazahmad Apr 1, 2022
b6d518c
merge main branch
mdneyazahmad May 1, 2022
21d3bc0
merge main branch
mdneyazahmad May 9, 2022
671f2d2
add comment for currencyList.json
mdneyazahmad May 10, 2022
7c4aa7e
refactor: IOUAmountPage
mdneyazahmad May 10, 2022
291782f
update comment in tests/unit/CurrencySymbolUtilsTest.js
mdneyazahmad May 12, 2022
1841022
refactor: TextInputWithCurrencySymbol
mdneyazahmad May 12, 2022
6147523
refactor: move inline function to class method
mdneyazahmad May 12, 2022
8e9764f
refactor: IOUAmountPage
mdneyazahmad May 12, 2022
46148a3
docs: add jsdocs
mdneyazahmad May 12, 2022
f85bbde
style: format code
mdneyazahmad May 17, 2022
04882ce
fix merge conflict
mdneyazahmad May 17, 2022
355e65d
refactor: remove unused code
mdneyazahmad May 17, 2022
fc91394
Merge branch 'fix/7915-localize-currency-symbol' of github.com:mdneya…
mdneyazahmad May 17, 2022
c25bf64
style: fix eslint error
mdneyazahmad May 17, 2022
133a62e
chore: update comment
mdneyazahmad May 17, 2022
f018a24
test: fix typo
mdneyazahmad Jun 2, 2022
c93b4cb
test: update comment
mdneyazahmad Jun 2, 2022
d87f569
fix: merge conflict
mdneyazahmad Jun 3, 2022
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
41 changes: 41 additions & 0 deletions src/libs/CurrencySymbolUtils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import _ from 'underscore';
import * as NumberFormatUtils from './NumberFormatUtils';

/**
* Get localized currency symbol for SO4217 Code
mountiny marked this conversation as resolved.
Show resolved Hide resolved
* @param {String} preferredLocale
* @param {String} currencyCode
* @return {String}
mountiny marked this conversation as resolved.
Show resolved Hide resolved
*/
function getLocalizedCurrencySymbol(preferredLocale, currencyCode) {
const parts = NumberFormatUtils.formatToParts(preferredLocale, 0, {
style: 'currency',
currency: currencyCode,
});
const currencySymbol = _.find(parts, part => part.type === 'currency').value;
return currencySymbol;
mountiny marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Is currency symbol to left
mountiny marked this conversation as resolved.
Show resolved Hide resolved
* @param {String} preferredLocale
* @param {String} currencyCode
* @return {Boolean}
mountiny marked this conversation as resolved.
Show resolved Hide resolved
*/
function isCurrencySymbolLTR(preferredLocale, currencyCode) {
const parts = NumberFormatUtils.formatToParts(preferredLocale, 0, {
style: 'currency',
currency: currencyCode,
});

// The first element of parts will be type: currency for all currency
// Where it starts with symbol and the other will have it at last
// If it is not the first, it must be at last
mountiny marked this conversation as resolved.
Show resolved Hide resolved
const isLeft = parts[0].type === 'currency';
return isLeft;
mountiny marked this conversation as resolved.
Show resolved Hide resolved
}

export {
getLocalizedCurrencySymbol,
isCurrencySymbolLTR,
};
3 changes: 2 additions & 1 deletion src/pages/iou/IOUCurrencySelection.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import KeyboardAvoidingView from '../../components/KeyboardAvoidingView';
import Button from '../../components/Button';
import FixedFooter from '../../components/FixedFooter';
import * as IOU from '../../libs/actions/IOU';
import * as CurrencySymbolUtils from '../../libs/CurrencySymbolUtils';

/**
* IOU Currency selection for selecting currency
Expand Down Expand Up @@ -113,7 +114,7 @@ class IOUCurrencySelection extends Component {
getCurrencyOptions() {
const currencyListKeys = _.keys(this.props.currencyList);
const currencyOptions = _.map(currencyListKeys, currencyCode => ({
text: `${currencyCode} - ${this.props.currencyList[currencyCode].symbol}`,
text: `${currencyCode} - ${CurrencySymbolUtils.getLocalizedCurrencySymbol(this.props.preferredLocale, currencyCode)}`,
searchText: `${currencyCode} ${this.props.currencyList[currencyCode].symbol}`,
currencyCode,
}));
Expand Down
62 changes: 29 additions & 33 deletions src/pages/iou/steps/IOUAmountPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import Text from '../../../components/Text';
import CONST from '../../../CONST';
import TextInput from '../../../components/TextInput';
import canUseTouchScreen from '../../../libs/canUseTouchscreen';
import * as CurrencySymbolUtils from '../../../libs/CurrencySymbolUtils';

const propTypes = {
/** Whether or not this IOU has multiple participants */
Expand All @@ -31,18 +32,6 @@ const propTypes = {
/** Callback to inform parent modal of success */
onStepComplete: PropTypes.func.isRequired,

/** The currency list constant object from Onyx */
currencyList: PropTypes.objectOf(PropTypes.shape({
/** Symbol for the currency */
symbol: PropTypes.string,

/** Name of the currency */
name: PropTypes.string,

/** ISO4217 Code for the currency */
ISO4217: PropTypes.string,
})).isRequired,

/** Previously selected amount to show if the user comes back to this screen */
selectedAmount: PropTypes.string.isRequired,

Expand Down Expand Up @@ -207,6 +196,33 @@ class IOUAmountPage extends React.Component {

render() {
const formattedAmount = this.replaceAllDigits(this.state.amount, this.props.toLocaleDigit);
const currencySymbol = CurrencySymbolUtils.getLocalizedCurrencySymbol(this.props.preferredLocale, this.props.iou.selectedCurrencyCode);
const isCurrencySymbolLTR = CurrencySymbolUtils.isCurrencySymbolLTR(this.props.preferredLocale, this.props.iou.selectedCurrencyCode);
mdneyazahmad marked this conversation as resolved.
Show resolved Hide resolved

const currencySymbolElement = (
<TouchableOpacity onPress={() => Navigation.navigate(this.props.hasMultipleParticipants
? ROUTES.getIouBillCurrencyRoute(this.props.reportID)
: ROUTES.getIouRequestCurrencyRoute(this.props.reportID))}
>
<Text style={styles.iouAmountText}>{currencySymbol}</Text>
</TouchableOpacity>
);

const amountTextInput = (
<TextInput
disableKeyboard
autoGrow
hideFocusedState
inputStyle={[styles.iouAmountTextInput, styles.p0, styles.noLeftBorderRadius, styles.noRightBorderRadius]}
textInputContainerStyles={[styles.borderNone, styles.noLeftBorderRadius, styles.noRightBorderRadius]}
onChangeText={this.updateAmount}
ref={el => this.textInput = el}
value={formattedAmount}
placeholder={this.props.numberFormat(0)}
keyboardType={CONST.KEYBOARD_TYPE.NUMBER_PAD}
/>
);

return (
<>
<View style={[
Expand All @@ -217,26 +233,7 @@ class IOUAmountPage extends React.Component {
styles.justifyContentCenter,
]}
>
<TouchableOpacity onPress={() => Navigation.navigate(this.props.hasMultipleParticipants
? ROUTES.getIouBillCurrencyRoute(this.props.reportID)
: ROUTES.getIouRequestCurrencyRoute(this.props.reportID))}
>
<Text style={styles.iouAmountText}>
{lodashGet(this.props.currencyList, [this.props.iou.selectedCurrencyCode, 'symbol'])}
</Text>
</TouchableOpacity>
<TextInput
disableKeyboard
autoGrow
hideFocusedState
inputStyle={[styles.iouAmountTextInput, styles.p0, styles.noLeftBorderRadius, styles.noRightBorderRadius]}
textInputContainerStyles={[styles.borderNone, styles.noLeftBorderRadius, styles.noRightBorderRadius]}
onChangeText={this.updateAmount}
ref={el => this.textInput = el}
value={formattedAmount}
placeholder={this.props.numberFormat(0)}
keyboardType={CONST.KEYBOARD_TYPE.NUMBER_PAD}
/>
{isCurrencySymbolLTR ? [currencySymbolElement, amountTextInput] : [amountTextInput, currencySymbolElement]}
mountiny marked this conversation as resolved.
Show resolved Hide resolved
</View>
<View style={[styles.w100, styles.justifyContentEnd]}>
{canUseTouchScreen()
Expand Down Expand Up @@ -266,7 +263,6 @@ IOUAmountPage.defaultProps = defaultProps;
export default compose(
withLocalize,
withOnyx({
currencyList: {key: ONYXKEYS.CURRENCY_LIST},
iou: {key: ONYXKEYS.IOU},
}),
)(IOUAmountPage);
33 changes: 33 additions & 0 deletions tests/unit/CurrencySymbolUtilsTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import _ from 'underscore';
import * as CurrencySymbolUtils from '../../src/libs/CurrencySymbolUtils';
import currencyList from './currencyList.json';

const currencyCodeList = _.keys(currencyList);

mountiny marked this conversation as resolved.
Show resolved Hide resolved
const AVAILABLE_LOCALES = ['en', 'es'];

// Contains item [isLeft, locale, currencyCode]
const symbolPositions = [
[true, 'en', 'USD'],
[false, 'es', 'USD'],
];

describe('CurrencySymbolUtils', () => {
describe('getLocalizedCurrencySymbol', () => {
test.each(AVAILABLE_LOCALES)('returns non empty string for all currencyCode with preferredLocale %s', (prefrredLocale) => {
mountiny marked this conversation as resolved.
Show resolved Hide resolved
_.forEach(currencyCodeList, (currencyCode) => {
const localizedSymbol = CurrencySymbolUtils.getLocalizedCurrencySymbol(prefrredLocale, currencyCode);

expect(typeof localizedSymbol).toBe('string');
expect(localizedSymbol.length).toBeGreaterThan(0);
mountiny marked this conversation as resolved.
Show resolved Hide resolved
});
});
});

describe('isCurrencySymbolLTR', () => {
test.each(symbolPositions)('returns %s for prefrredLocale %s and currencyCode %s', (isLeft, locale, currencyCode) => {
mountiny marked this conversation as resolved.
Show resolved Hide resolved
const isSymbolLeft = CurrencySymbolUtils.isCurrencySymbolLTR(locale, currencyCode);
expect(isSymbolLeft).toBe(isLeft);
});
});
});
Loading