Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Rex Dri
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
32
Issues
32
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Environments
Packages & Registries
Packages & Registries
Container Registry
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Rex Dri
Rex Dri
Commits
ff8b40ab
Commit
ff8b40ab
authored
Feb 27, 2019
by
Florent Chehab
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'handle_money_in_markdown' into 'master'
Handle money in markdown See merge request
!56
parents
114ea1cf
1227c0ff
Pipeline
#35570
passed with stages
in 5 minutes and 35 seconds
Changes
7
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
237 additions
and
24 deletions
+237
-24
frontend/src/components/map/UnivMap.js
frontend/src/components/map/UnivMap.js
+18
-12
frontend/src/components/pages/PageHome.js
frontend/src/components/pages/PageHome.js
+5
-0
frontend/src/components/shared/Markdown.js
frontend/src/components/shared/Markdown.js
+70
-9
frontend/src/components/university/shared/Scholarship.js
frontend/src/components/university/shared/Scholarship.js
+3
-3
frontend/src/utils/convertAmountToEur.js
frontend/src/utils/convertAmountToEur.js
+17
-0
frontend/src/utils/parseMoney.js
frontend/src/utils/parseMoney.js
+73
-0
frontend/tests/utils/parseMoney.test.js
frontend/tests/utils/parseMoney.test.js
+51
-0
No files found.
frontend/src/components/map/UnivMap.js
View file @
ff8b40ab
import
React
from
"
react
"
;
import
CustomComponentForAPI
from
"
../CustomComponentForAPI
"
;
import
React
,
{
Component
}
from
"
react
"
;
import
{
connect
}
from
"
react-redux
"
;
import
{
Map
,
TileLayer
,
LayersControl
,
LayerGroup
}
from
"
react-leaflet
"
;
import
PropTypes
from
"
prop-types
"
;
import
{
Map
,
TileLayer
,
LayersControl
,
LayerGroup
}
from
"
react-leaflet
"
;
import
UnivMarkers
from
"
./UnivMakers
"
;
import
{
saveMainMapStatus
}
from
"
../../actions/map
"
;
...
...
@@ -12,9 +12,9 @@ import { saveMainMapStatus } from "../../actions/map";
* Component to create the map of universities
*
* @class UnivMap
* @extends {C
ustomComponentForAPI
}
* @extends {C
omponent
}
*/
class
UnivMap
extends
C
ustomComponentForAPI
{
class
UnivMap
extends
C
omponent
{
// Initial state
state
=
{
...
...
@@ -22,6 +22,12 @@ class UnivMap extends CustomComponentForAPI {
height
:
800
,
}
constructor
(
props
)
{
super
(
props
);
// Make sure to set the correct height on mount
this
.
updateDimensions
();
}
/**
* Custom function to update the appropriate height of the map
*
...
...
@@ -37,15 +43,10 @@ class UnivMap extends CustomComponentForAPI {
catch
(
err
)
{
}
}
componentWillMount
()
{
// Make sure to set the correct height on mount
this
.
updateDimensions
();
}
componentDidMount
()
{
// add an event listener to resize the map when needed
window
.
addEventListener
(
"
resize
"
,
this
.
updateDimensions
.
bind
(
this
));
super
.
componentDidMount
();
this
.
updateDimensions
();
}
componentWillUnmount
()
{
...
...
@@ -93,7 +94,7 @@ class UnivMap extends CustomComponentForAPI {
}));
}
customR
ender
()
{
r
ender
()
{
const
stamenName
=
"
Stamen Watercolor
"
,
osmFrName
=
"
OpenStreetMap France
"
,
esriName
=
"
Esri WorldImagery
"
,
...
...
@@ -154,6 +155,11 @@ class UnivMap extends CustomComponentForAPI {
}
UnivMap
.
propTypes
=
{
map
:
PropTypes
.
object
.
isRequired
,
saveMainMap
:
PropTypes
.
func
.
isRequired
};
const
mapStateToProps
=
(
state
)
=>
{
return
{
map
:
state
.
app
.
mainMap
...
...
frontend/src/components/pages/PageHome.js
View file @
ff8b40ab
...
...
@@ -49,6 +49,11 @@ Les objectifs de ce service sont :
| Ancien départs | ✔ |
| Départs possibles | ✔ |
| Informatio sur les universités | ✔ |
## Autre fun feature
You can format money: \`:100CHF:\` => :100CHF:
`
;
...
...
frontend/src/components/shared/Markdown.js
View file @
ff8b40ab
...
...
@@ -2,8 +2,13 @@
/* eslint-disable react/display-name */
// Inspired by : https://github.com/mui-org/material-ui/blob/master/docs/src/pages/page-layout-examples/blog/Markdown.js
import
React
from
"
react
"
;
import
React
,
{
Component
}
from
"
react
"
;
import
{
connect
}
from
"
react-redux
"
;
import
PropTypes
from
"
prop-types
"
;
import
ReactMarkdown
from
"
react-markdown
"
;
import
parseMoney
from
"
../../utils/parseMoney
"
;
import
convertAmountToEur
from
"
../../utils/convertAmountToEur
"
;
import
withStyles
from
"
@material-ui/core/styles/withStyles
"
;
import
Typography
from
"
@material-ui/core/Typography
"
;
...
...
@@ -175,15 +180,71 @@ const renderers = {
tableBody
:
props
=>
(
<
TableBody
>
{
props
.
children
}
<
/TableBody>
)
,
tableRow
:
props
=>
(
<
TableRow
hover
=
{
true
}
>
{
props
.
children
}
<
/TableRow>
)
,
tableCell
:
props
=>
(
<
TableCell
>
{
props
.
children
}
<
/TableCell>
)
,
thematicBreak
:
()
=>
(
<
Divider
/>
)
thematicBreak
:
()
=>
(
<
Divider
/>
)
,
};
export
default
function
Markdown
(
props
)
{
return
<
ReactMarkdown
renderers
=
{
renderers
}
allowedTypes
=
{[...
Object
.
keys
(
renderers
),
"
text
"
,
"
root
"
,
"
strong
"
]}
// Only allow custom nodes and basic ones
mode
=
{
"
escape
"
}
{...
props
}
/>
;
/**
* Custom Markdown component renderer to make use of material UI
*
* We don't make use of Custom Component for API since currencies should be loaded at app startup.
* We don't need to fetch them here
*
* @class Markdown
* @extends {Component}
*/
class
Markdown
extends
Component
{
/**
* Function to "compile" the markdown source
* It adds the money conversion information if the custom tag is present.
*
* @param {string} source
* @returns {string}
* @memberof Markdown
*/
compileSource
(
source
)
{
let
compiled
=
""
;
parseMoney
(
source
).
forEach
(
el
=>
{
if
(
!
el
.
isMoney
)
{
compiled
+=
el
.
text
;
}
else
{
const
{
amount
,
currency
}
=
el
,
{
currencies
}
=
this
.
props
;
if
(
currency
===
"
EUR
"
)
{
compiled
+=
`
${
amount
}
€`
;
}
else
{
const
converted
=
convertAmountToEur
(
amount
,
currency
,
currencies
);
compiled
+=
`
${
amount
}${
currency
}
[*(≈
${
converted
}
€)*](https://www.xe.com/currencyconverter/convert/?Amount=
${
amount
}
&From=
${
currency
}
&To=EUR)`
;
// add money converted information in markdown format
}
}
});
return
compiled
;
}
render
()
{
const
compiledSource
=
this
.
compileSource
(
this
.
props
.
source
);
return
<
ReactMarkdown
renderers
=
{
renderers
}
allowedTypes
=
{[...
Object
.
keys
(
renderers
),
"
text
"
,
"
emphasis
"
,
"
root
"
,
"
strong
"
]}
// Only allow custom nodes and basic ones
mode
=
{
"
escape
"
}
source
=
{
compiledSource
}
/>
;
}
}
Markdown
.
propTypes
=
{
currencies
:
PropTypes
.
array
.
isRequired
,
source
:
PropTypes
.
string
};
const
mapStateToPropsTextRenderer
=
(
state
)
=>
({
currencies
:
state
.
api
.
currenciesAll
.
readSucceeded
.
data
,
});
export
default
connect
(
mapStateToPropsTextRenderer
)(
Markdown
);
frontend/src/components/university/shared/Scholarship.js
View file @
ff8b40ab
...
...
@@ -3,6 +3,7 @@ import PropTypes from "prop-types";
import
withStyles
from
"
@material-ui/core/styles/withStyles
"
;
import
Markdown
from
"
../../shared/Markdown
"
;
import
moneyConversion
from
"
../../../utils/convertAmountToEur
"
;
import
Typography
from
"
@material-ui/core/Typography
"
;
const
styles
=
theme
=>
{
...
...
@@ -63,9 +64,8 @@ class Scholarship extends React.Component {
}
convertAmountToEur
(
amount
)
{
const
{
currencies
,
currency
}
=
this
.
props
;
const
rate
=
currencies
.
find
(
c
=>
c
.
id
==
currency
).
one_EUR_in_this_currency
;
return
Math
.
trunc
(
amount
/
rate
);
const
{
currency
,
currencies
}
=
this
.
props
;
return
moneyConversion
(
amount
,
currency
,
currencies
);
}
getAmounts
()
{
...
...
frontend/src/utils/convertAmountToEur.js
0 → 100644
View file @
ff8b40ab
/**
* Function for converting money amounts to euros.
*
* @export
* @param {number} amount
* @param {string} currency
* @param {Array[string]} currencies
* @returns {number}
*/
export
default
function
convertAmountToEur
(
amount
,
currency
,
currencies
)
{
if
(
currency
===
"
EUR
"
)
{
return
amount
;
}
const
rate
=
currencies
.
find
(
c
=>
c
.
id
==
currency
).
one_EUR_in_this_currency
;
return
Math
.
trunc
(
amount
/
rate
);
}
frontend/src/utils/parseMoney.js
0 → 100644
View file @
ff8b40ab
/**
* Function to get a regex object for money parsing
*
* @returns
*/
function
getMoneyRegex
()
{
return
/
(?<
!`
)
:
(\d
*
[
.,
]?\d
*
)(\w{3})
:/g
;
}
/**
* Parses a string to determine if there are some currency in it.
*
* For example, the string: "Hi, I earn :10.15CHF:" will be converted to:
* [{ isMoney: false, text: 'Hi, I earn ' },
* { isMoney: true, amount: '10.15', currency: 'CHF' }]
*
* In the string: amount can be an int, a float with ',' or '.' as separator
* And the currency can be in mixed case, but will always be return in uppercase.
*
* @export
* @param {string} str
* @returns {Array}
*/
export
default
function
parseMoney
(
str
)
{
if
(
str
===
""
)
{
return
[];
}
// reusable function
const
getOutputText
=
(
str
)
=>
({
isMoney
:
false
,
text
:
str
});
if
(
!
getMoneyRegex
().
test
(
str
))
{
// if the string doesn't contain anything interesting
return
[
getOutputText
(
str
)];
}
else
{
let
matches
=
[],
match
,
moneyRegEx
=
getMoneyRegex
();
while
((
match
=
moneyRegEx
.
exec
(
str
))
!==
null
)
{
const
matchStartIndex
=
match
.
index
,
// index of the starting ':'
matchLastIndex
=
moneyRegEx
.
lastIndex
-
1
,
// index of the ending ':'
amount
=
parseFloat
(
match
[
"
1
"
].
replace
(
"
,
"
,
"
.
"
)),
// fix numbers with "," as decimal separators
currency
=
match
[
"
2
"
].
toUpperCase
();
// make sure the currency is uppercase
matches
.
push
({
matchStartIndex
,
matchLastIndex
,
amount
,
currency
});
}
let
res
=
[],
lastIndex
=
0
;
matches
.
forEach
((
el
)
=>
{
if
(
lastIndex
!==
el
.
matchStartIndex
)
{
// we need to add a classic string that was before the currency marker
res
.
push
(
getOutputText
(
str
.
substring
(
lastIndex
,
el
.
matchStartIndex
)));
}
// We add the element corresponding to money mount
res
.
push
({
isMoney
:
true
,
amount
:
el
.
amount
,
currency
:
el
.
currency
});
lastIndex
=
el
.
matchLastIndex
+
1
;
});
// we need to add the eventual trailing text:
if
(
lastIndex
!==
str
.
length
)
{
res
.
push
(
getOutputText
(
str
.
substring
(
lastIndex
)));
}
return
res
;
}
}
frontend/tests/utils/parseMoney.test.js
0 → 100644
View file @
ff8b40ab
import
parseMoney
from
"
../../src/utils/parseMoney
"
;
test
(
"
parse empty string
"
,
()
=>
{
const
str
=
""
;
expect
(
parseMoney
(
str
).
length
).
toBe
(
0
);
});
test
(
"
Parse string with no money
"
,
()
=>
{
const
str
=
"
A random classic string
"
;
expect
(
parseMoney
(
str
).
length
).
toBe
(
1
);
expect
(
parseMoney
(
str
)[
0
].
text
).
toBe
(
str
);
});
test
(
"
Parse string with only money
"
,
()
=>
{
const
str
=
"
:100CHF:
"
,
parsed
=
parseMoney
(
str
);
expect
(
parsed
.
length
).
toBe
(
1
);
expect
(
parsed
[
0
].
amount
).
toBe
(
100
);
expect
(
parsed
[
0
].
currency
).
toBe
(
"
CHF
"
);
});
test
(
"
Parse complicated string
"
,
()
=>
{
const
str
=
"
Hi, I earn :0,0Chf: but he earned :100.12EUR: this year !
"
,
parsed
=
parseMoney
(
str
);
expect
(
parsed
.
length
).
toBe
(
5
);
expect
(
parsed
[
0
].
isMoney
).
toBe
(
false
);
expect
(
parsed
[
1
].
isMoney
).
toBe
(
true
);
expect
(
parsed
[
2
].
isMoney
).
toBe
(
false
);
expect
(
parsed
[
3
].
isMoney
).
toBe
(
true
);
expect
(
parsed
[
4
].
isMoney
).
toBe
(
false
);
expect
(
parsed
[
0
].
text
).
toBe
(
"
Hi, I earn
"
);
expect
(
parsed
[
2
].
text
).
toBe
(
"
but he earned
"
);
expect
(
parsed
[
4
].
text
).
toBe
(
"
this year !
"
);
expect
(
parsed
[
1
].
amount
).
toBe
(
0
);
expect
(
parsed
[
3
].
amount
).
toBe
(
100.12
);
expect
(
parsed
[
1
].
currency
).
toBe
(
"
CHF
"
);
expect
(
parsed
[
3
].
currency
).
toBe
(
"
EUR
"
);
});
test
(
"
Money directly in code is returned as text
"
,
()
=>
{
const
str
=
"
You can use `:120CHF:` to tag money infos
"
,
parsed
=
parseMoney
(
str
);
expect
(
parsed
.
length
).
toBe
(
1
);
expect
(
parsed
[
0
].
isMoney
).
toBe
(
false
);
});
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment