Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Julien Jerphanion
Rex Dri
Commits
7266140c
Commit
7266140c
authored
Feb 24, 2019
by
Florent Chehab
Browse files
Merge branch 'cleaning' into 'master'
Cleaning See merge request
rex-dri/rex-dri!50
parents
0e42118e
4fe1bdf2
Changes
106
Show whitespace changes
Inline
Side-by-side
frontend/src/api/SmartActions.js
View file @
7266140c
import
Cookies
from
"
js-cookie
"
;
import
Cookies
from
"
js-cookie
"
;
// TODO uodate parameters name to match new CRUD actions
/**
* Class to perform network request and store the "status" in the redux store.
* The request are performed in a smart way to prevent multiple request to same endpoint at the same time.
*
* @export
* @class SmartActions
*/
export
default
class
SmartActions
{
export
default
class
SmartActions
{
fetching
=
new
Map
();
fetching
=
new
Map
();
...
@@ -22,51 +27,51 @@ export default class SmartActions {
...
@@ -22,51 +27,51 @@ export default class SmartActions {
* Generic function for handling a GET request to the API
* Generic function for handling a GET request to the API
*
*
* @param {string} pk
* @param {string} pk
* @param {string} api
_end_p
oint
* @param {string} api
EndP
oint
* @param {function}
_IsLo
ading
* @param {function}
isRe
ading
* @param {function}
_Fetch
DataSucce
ss
* @param {function}
read
DataSucce
eded
* @param {function}
_I
nvalidate
d
* @param {function}
i
nvalidate
* @param {function}
_HasError
* @param {function}
failed
* @param {boolean} [pk
_r
equired=false]
* @param {boolean} [pk
R
equired=false]
* @returns {function} Function that takes the `dispatch` function of redux as argument.
* @returns {function} Function that takes the `dispatch` function of redux as argument.
* @memberof SmartActions
* @memberof SmartActions
*/
*/
_FetchData
(
pk
,
api
_end_p
oint
,
_IsLo
ading
,
_Fetch
DataSucce
ss
,
_I
nvalidate
d
,
_HasError
,
pk
_r
equired
=
false
)
{
_FetchData
(
pk
,
api
EndP
oint
,
isRe
ading
,
read
DataSucce
eded
,
i
nvalidate
,
failed
,
pk
R
equired
=
false
)
{
if
(
pk
_r
equired
&&
(
typeof
pk
==
"
undefined
"
))
{
if
(
pk
R
equired
&&
(
typeof
pk
==
"
undefined
"
))
{
throw
"
pk shouldn't be empty when requesting a specific element
"
;
throw
"
pk shouldn't be empty when requesting a specific element
"
;
}
}
if
(
pk
!=
""
)
{
if
(
pk
!=
""
)
{
api
_end_p
oint
+=
pk
+
"
/
"
;
api
EndP
oint
+=
pk
+
"
/
"
;
}
}
if
(
!
this
.
shouldFetchEndPoint
(
api
_end_p
oint
))
{
if
(
!
this
.
shouldFetchEndPoint
(
api
EndP
oint
))
{
return
()
=>
{
};
return
()
=>
{
};
}
else
{
}
else
{
this
.
fetching
.
set
(
api
_end_p
oint
,
true
);
this
.
fetching
.
set
(
api
EndP
oint
,
true
);
}
}
return
(
dispatch
)
=>
{
return
(
dispatch
)
=>
{
dispatch
(
_IsLo
ading
(
true
));
dispatch
(
isRe
ading
(
true
));
let
token
=
Cookies
.
get
(
"
csrftoken
"
);
let
token
=
Cookies
.
get
(
"
csrftoken
"
);
fetch
(
api
_end_p
oint
,
{
credentials
:
"
same-origin
"
,
headers
:
{
"
X-CSRFToken
"
:
token
}
})
fetch
(
api
EndP
oint
,
{
credentials
:
"
same-origin
"
,
headers
:
{
"
X-CSRFToken
"
:
token
}
})
.
then
((
response
)
=>
{
.
then
((
response
)
=>
{
if
(
!
response
.
ok
)
{
if
(
!
response
.
ok
)
{
this
.
fetching
.
delete
(
api
_end_p
oint
);
this
.
fetching
.
delete
(
api
EndP
oint
);
throw
Error
(
response
.
statusText
);
throw
Error
(
response
.
statusText
);
}
}
return
response
;
return
response
;
})
})
.
then
((
response
)
=>
response
.
json
())
.
then
((
response
)
=>
response
.
json
())
.
then
((
obj
)
=>
{
.
then
((
obj
)
=>
{
dispatch
(
_I
nvalidate
d
(
false
));
dispatch
(
i
nvalidate
(
false
));
dispatch
(
_Fetch
DataSucce
ss
(
obj
));
dispatch
(
read
DataSucce
eded
(
obj
));
dispatch
(
_IsLo
ading
(
false
));
dispatch
(
isRe
ading
(
false
));
this
.
fetching
.
delete
(
api
_end_p
oint
);
this
.
fetching
.
delete
(
api
EndP
oint
);
})
})
.
catch
((
e
)
=>
{
.
catch
((
e
)
=>
{
dispatch
(
_HasError
(
true
,
e
));
dispatch
(
failed
(
true
,
e
));
dispatch
(
_IsLo
ading
(
false
));
dispatch
(
isRe
ading
(
false
));
this
.
fetching
.
delete
(
api
_end_p
oint
);
this
.
fetching
.
delete
(
api
EndP
oint
);
});
});
};
};
}
}
...
@@ -77,14 +82,14 @@ export default class SmartActions {
...
@@ -77,14 +82,14 @@ export default class SmartActions {
* Generic function for handling a PUT and POST requests to the API
* Generic function for handling a PUT and POST requests to the API
*
*
* @param {object} data, to update the data, `data` should contains the `id` field.
* @param {object} data, to update the data, `data` should contains the `id` field.
* @param {string} api
_end_p
oint
* @param {string} api
EndP
oint
* @param {function}
_ElI
sSaving
* @param {function}
i
sSaving
Specific
* @param {function}
_ElFetchData
Succe
ss
* @param {function}
readSpecific
Succe
eded
* @param {function}
_ElHasError
* @param {function}
failedSpecific
* @returns {function} Function that takes the `dispatch` function of redux as argument.
* @returns {function} Function that takes the `dispatch` function of redux as argument.
* @memberof SmartActions
* @memberof SmartActions
*/
*/
_ElSaveData
(
data
,
api
_end_p
oint
,
_ElI
sSaving
,
_ElFetchDataSuccess
,
_ElHasError
)
{
_ElSaveData
(
data
,
api
EndP
oint
,
i
sSaving
Specific
,
readSpecificSucceeded
,
failedSpecific
)
{
return
(
dispatch
)
=>
{
return
(
dispatch
)
=>
{
let
method
=
"
POST
"
;
let
method
=
"
POST
"
;
let
pk
=
""
;
let
pk
=
""
;
...
@@ -99,9 +104,9 @@ export default class SmartActions {
...
@@ -99,9 +104,9 @@ export default class SmartActions {
let
errorStatusText
=
""
;
let
errorStatusText
=
""
;
dispatch
(
_ElI
sSaving
(
true
));
dispatch
(
i
sSaving
Specific
(
true
));
let
token
=
Cookies
.
get
(
"
csrftoken
"
);
let
token
=
Cookies
.
get
(
"
csrftoken
"
);
fetch
(
api
_end_p
oint
+
__apiAttr
+
pk
,
{
fetch
(
api
EndP
oint
+
__apiAttr
+
pk
,
{
method
:
method
,
method
:
method
,
credentials
:
"
same-origin
"
,
credentials
:
"
same-origin
"
,
headers
:
{
headers
:
{
...
@@ -119,8 +124,8 @@ export default class SmartActions {
...
@@ -119,8 +124,8 @@ export default class SmartActions {
return
response
.
json
();
return
response
.
json
();
})
})
.
then
((
_El
)
=>
{
.
then
((
_El
)
=>
{
dispatch
(
_ElFetchData
Succe
ss
(
_El
));
// we use the same here
dispatch
(
readSpecific
Succe
eded
(
_El
));
// we use the same here
dispatch
(
_ElI
sSaving
(
false
));
dispatch
(
i
sSaving
Specific
(
false
));
})
})
.
catch
((
e
)
=>
{
.
catch
((
e
)
=>
{
if
(
typeof
e
.
json
==
"
function
"
)
{
if
(
typeof
e
.
json
==
"
function
"
)
{
...
@@ -132,8 +137,8 @@ export default class SmartActions {
...
@@ -132,8 +137,8 @@ export default class SmartActions {
})
})
.
then
((
errorContent
)
=>
{
.
then
((
errorContent
)
=>
{
if
(
typeof
errorContent
!=
"
undefined
"
)
{
if
(
typeof
errorContent
!=
"
undefined
"
)
{
dispatch
(
_ElHasError
(
true
,
{
message
:
errorStatusText
,
content
:
errorContent
}));
dispatch
(
failedSpecific
(
true
,
{
message
:
errorStatusText
,
content
:
errorContent
}));
dispatch
(
_ElI
sSaving
(
false
));
dispatch
(
i
sSaving
Specific
(
false
));
}
}
});
});
};
};
...
...
frontend/src/api/buildApiActionsAndReducers.js
View file @
7266140c
/*
* This file dynamically create the redux actions and reducers related to the REST API and custom apis
*/
import
API_CONFIG
from
"
../../../shared/api_config.yml
"
;
import
API_CONFIG
from
"
../../../shared/api_config.yml
"
;
import
CrudActions
from
"
./CrudActions
"
;
import
CrudActions
from
"
./CrudActions
"
;
import
CrudReducers
from
"
./CrudReducers
"
;
import
CrudReducers
from
"
./CrudReducers
"
;
...
@@ -6,7 +10,6 @@ import CrudReducers from "./CrudReducers";
...
@@ -6,7 +10,6 @@ import CrudReducers from "./CrudReducers";
let
apiActionsTmp
=
{};
let
apiActionsTmp
=
{};
let
apiReducersTmp
=
{};
let
apiReducersTmp
=
{};
// Simple APIs outside rest framework
// Simple APIs outside rest framework
// TODO merge with the shared file
// TODO merge with the shared file
const
otherAPI
=
[
const
otherAPI
=
[
...
@@ -30,7 +33,6 @@ function addAPIs(arr) {
...
@@ -30,7 +33,6 @@ function addAPIs(arr) {
// TODO better generality with /api add to a shared parameter file
// TODO better generality with /api add to a shared parameter file
let
apiEndPoint
=
`/api/
${
info
.
api_end_point
}
/`
;
let
apiEndPoint
=
`/api/
${
info
.
api_end_point
}
/`
;
let
apiInfo
=
{
let
apiInfo
=
{
name
:
info
.
api_end_point
,
name
:
info
.
api_end_point
,
readOnly
:
info
.
read_only
,
readOnly
:
info
.
read_only
,
...
...
frontend/src/components/App.js
View file @
7266140c
// Inspired by https://github.com/mui-org/material-ui/tree/master/docs/src/pages/page-layout-examples/dashboard
/** General app JS entry
* Inspired by https://github.com/mui-org/material-ui/tree/master/docs/src/pages/page-layout-examples/dashboard
*/
import
React
from
"
react
"
;
import
React
from
"
react
"
;
import
PropTypes
from
"
prop-types
"
;
import
PropTypes
from
"
prop-types
"
;
...
@@ -19,7 +21,6 @@ import { mainListItems, secondaryListItems, thirdListItems } from "./template/li
...
@@ -19,7 +21,6 @@ import { mainListItems, secondaryListItems, thirdListItems } from "./template/li
import
{
connect
}
from
"
react-redux
"
;
import
{
connect
}
from
"
react-redux
"
;
import
CustomComponentForAPI
from
"
./CustomComponentForAPI
"
;
import
CustomComponentForAPI
from
"
./CustomComponentForAPI
"
;
// import route Components here
import
{
import
{
Route
,
Route
,
Redirect
Redirect
...
@@ -33,82 +34,13 @@ import PageUniversity from "./pages/PageUniversity";
...
@@ -33,82 +34,13 @@ import PageUniversity from "./pages/PageUniversity";
import
PageSearch
from
"
./pages/PageSearch
"
;
import
PageSearch
from
"
./pages/PageSearch
"
;
import
PageSettings
from
"
./pages/PageSettings
"
;
import
PageSettings
from
"
./pages/PageSettings
"
;
const
drawerWidth
=
240
;
const
DRAWER_WIDTH
=
240
;
const
styles
=
theme
=>
({
root
:
{
display
:
"
flex
"
,
},
toolbar
:
{
paddingRight
:
24
,
// keep right padding when drawer closed
},
toolbarIcon
:
{
display
:
"
flex
"
,
alignItems
:
"
center
"
,
justifyContent
:
"
flex-end
"
,
padding
:
"
0 8px
"
,
...
theme
.
mixins
.
toolbar
,
},
chip
:
{
margin
:
theme
.
spacing
.
unit
,
},
menuButton
:
{
marginRight
:
4
,
},
hideIt
:
{
display
:
"
none
"
,
},
title
:
{
flexGrow
:
1
,
},
drawerPaper
:
{
position
:
"
relative
"
,
whiteSpace
:
"
nowrap
"
,
width
:
drawerWidth
,
transition
:
theme
.
transitions
.
create
(
"
width
"
,
{
easing
:
theme
.
transitions
.
easing
.
sharp
,
duration
:
theme
.
transitions
.
duration
.
enteringScreen
,
}),
},
drawerPaperClose
:
{
overflowX
:
"
hidden
"
,
transition
:
theme
.
transitions
.
create
(
"
width
"
,
{
easing
:
theme
.
transitions
.
easing
.
sharp
,
duration
:
theme
.
transitions
.
duration
.
leavingScreen
,
}),
width
:
theme
.
spacing
.
unit
*
7
,
[
theme
.
breakpoints
.
up
(
"
sm
"
)]:
{
width
:
theme
.
spacing
.
unit
*
9
,
},
},
content
:
{
flexGrow
:
1
,
padding
:
theme
.
spacing
.
unit
*
3
,
height
:
"
100vh
"
,
overflow
:
"
auto
"
,
paddingTop
:
"
0px
"
},
paddingTop
:
{
paddingTop
:
"
24px
"
},
chartContainer
:
{
marginLeft
:
-
22
,
},
tableContainer
:
{
height
:
320
,
},
myPaper
:
{
padding
:
16
},
null
:
{}
});
class
App
extends
CustomComponentForAPI
{
class
App
extends
CustomComponentForAPI
{
state
=
{
state
=
{
open
:
true
,
open
:
true
,
};
};
handleDrawerOpen
=
()
=>
{
handleDrawerOpen
=
()
=>
{
this
.
setState
({
open
:
true
});
this
.
setState
({
open
:
true
});
};
};
...
@@ -123,6 +55,7 @@ class App extends CustomComponentForAPI {
...
@@ -123,6 +55,7 @@ class App extends CustomComponentForAPI {
return
(
return
(
<
React
.
Fragment
>
<
React
.
Fragment
>
<
CssBaseline
/>
<
CssBaseline
/>
<
div
className
=
{
classes
.
root
}
>
<
div
className
=
{
classes
.
root
}
>
<
Drawer
<
Drawer
...
@@ -132,8 +65,8 @@ class App extends CustomComponentForAPI {
...
@@ -132,8 +65,8 @@ class App extends CustomComponentForAPI {
}}
}}
open
=
{
this
.
state
.
open
}
open
=
{
this
.
state
.
open
}
>
>
<
div
className
=
{
classNames
(
classes
.
toolbarIcon
)
}
>
<
div
className
=
{
classes
.
toolbarIcon
}
>
<
div
className
=
{
classNames
((
!
this
.
state
.
open
)
&&
classes
.
hideIt
,
classes
.
null
)
}
>
<
div
className
=
{
!
this
.
state
.
open
?
classes
.
hideIt
:
classes
.
null
}
>
<
Chip
<
Chip
avatar
=
{
<
Avatar
>
<
SchoolIcon
/>
<
/Avatar>
}
avatar
=
{
<
Avatar
>
<
SchoolIcon
/>
<
/Avatar>
}
label
=
"
Outgoing REX
"
label
=
"
Outgoing REX
"
...
@@ -141,12 +74,24 @@ class App extends CustomComponentForAPI {
...
@@ -141,12 +74,24 @@ class App extends CustomComponentForAPI {
color
=
"
primary
"
color
=
"
primary
"
/>
/>
<
/div
>
<
/div
>
<
IconButton
onClick
=
{
this
.
handleDrawerOpen
}
className
=
{
classNames
(
classes
.
menuButton
,
<
IconButton
this
.
state
.
open
&&
classes
.
hideIt
)}
>
onClick
=
{
this
.
handleDrawerOpen
}
className
=
{
classNames
(
classes
.
menuButton
,
this
.
state
.
open
?
classes
.
hideIt
:
classes
.
null
)}
>
<
MenuIcon
/>
<
MenuIcon
/>
<
/IconButton
>
<
/IconButton
>
<
IconButton
onClick
=
{
this
.
handleDrawerClose
}
className
=
{
classNames
(
classes
.
menuButton
,
<
IconButton
(
!
this
.
state
.
open
)
&&
classes
.
hideIt
)}
>
onClick
=
{
this
.
handleDrawerClose
}
className
=
{
classNames
(
classes
.
menuButton
,
!
this
.
state
.
open
?
classes
.
hideIt
:
classes
.
null
)}
>
<
ChevronLeftIcon
/>
<
ChevronLeftIcon
/>
<
/IconButton
>
<
/IconButton
>
<
/div
>
<
/div
>
...
@@ -158,6 +103,7 @@ class App extends CustomComponentForAPI {
...
@@ -158,6 +103,7 @@ class App extends CustomComponentForAPI {
<
List
>
{
secondaryListItems
}
<
/List
>
<
List
>
{
secondaryListItems
}
<
/List
>
<
Divider
/>
<
Divider
/>
<
List
>
{
thirdListItems
}
<
/List
>
<
List
>
{
thirdListItems
}
<
/List
>
<
/Drawer
>
<
/Drawer
>
<
main
className
=
{
classNames
(
classes
.
content
,
classes
.
noPaddingTop
)}
>
<
main
className
=
{
classNames
(
classes
.
content
,
classes
.
noPaddingTop
)}
>
...
@@ -176,6 +122,7 @@ class App extends CustomComponentForAPI {
...
@@ -176,6 +122,7 @@ class App extends CustomComponentForAPI {
<
Route
path
=
"
/app/university/:id
"
component
=
{
PageUniversity
}
/
>
<
Route
path
=
"
/app/university/:id
"
component
=
{
PageUniversity
}
/
>
<
/div
>
<
/div
>
<
/main
>
<
/main
>
<
/div
>
<
/div
>
<
/React.Fragment
>
<
/React.Fragment
>
);
);
...
@@ -203,4 +150,71 @@ const mapDispatchToProps = (dispatch) => {
...
@@ -203,4 +150,71 @@ const mapDispatchToProps = (dispatch) => {
};
};
};
};
const
styles
=
theme
=>
({
root
:
{
display
:
"
flex
"
,
},
toolbar
:
{
paddingRight
:
24
,
// keep right padding when drawer closed
},
toolbarIcon
:
{
display
:
"
flex
"
,
alignItems
:
"
center
"
,
justifyContent
:
"
flex-end
"
,
padding
:
"
0 8px
"
,
...
theme
.
mixins
.
toolbar
,
},
chip
:
{
margin
:
theme
.
spacing
.
unit
,
},
menuButton
:
{
marginRight
:
4
,
},
hideIt
:
{
display
:
"
none
"
,
},
title
:
{
flexGrow
:
1
,
},
drawerPaper
:
{
position
:
"
relative
"
,
whiteSpace
:
"
nowrap
"
,
width
:
DRAWER_WIDTH
,
transition
:
theme
.
transitions
.
create
(
"
width
"
,
{
easing
:
theme
.
transitions
.
easing
.
sharp
,
duration
:
theme
.
transitions
.
duration
.
enteringScreen
,
}),
},
drawerPaperClose
:
{
overflowX
:
"
hidden
"
,
transition
:
theme
.
transitions
.
create
(
"
width
"
,
{
easing
:
theme
.
transitions
.
easing
.
sharp
,
duration
:
theme
.
transitions
.
duration
.
leavingScreen
,
}),
width
:
theme
.
spacing
.
unit
*
7
,
[
theme
.
breakpoints
.
up
(
"
sm
"
)]:
{
width
:
theme
.
spacing
.
unit
*
9
,
},
},
content
:
{
flexGrow
:
1
,
padding
:
theme
.
spacing
.
unit
*
3
,
height
:
"
100vh
"
,
overflow
:
"
auto
"
,
paddingTop
:
"
0px
"
},
paddingTop
:
{
paddingTop
:
"
24px
"
},
chartContainer
:
{
marginLeft
:
-
22
,
},
tableContainer
:
{
height
:
320
,
},
null
:
{}
});
export
default
connect
(
mapStateToProps
,
mapDispatchToProps
)(
withStyles
(
styles
,
{
withTheme
:
true
})(
App
));
export
default
connect
(
mapStateToProps
,
mapDispatchToProps
)(
withStyles
(
styles
,
{
withTheme
:
true
})(
App
));
frontend/src/components/CustomComponentForAPI.js
View file @
7266140c
...
@@ -192,7 +192,7 @@ class CustomComponentForAPI extends Component {
...
@@ -192,7 +192,7 @@ class CustomComponentForAPI extends Component {
{ readAt: 0 });
{ readAt: 0 });
if (!("data" in out)) {
if (!("data" in out)) {
throw Error(`No read data from the api could be retr
e
ived for: ${propName}`);
throw Error(`No read data from the api could be retri
e
ved for: ${propName}`);
} else {
} else {
return out;
return out;
}
}
...
@@ -210,6 +210,17 @@ class CustomComponentForAPI extends Component {
...
@@ -210,6 +210,17 @@ class CustomComponentForAPI extends Component {
return this.getReadDataAndTime(propName).data;
return this.getReadDataAndTime(propName).data;
}
}
/**
* Get the time at which the latest data from the api corresponding to `propName` was read
*
* @param {string} propName
* @returns {Any}
* @memberof CustomComponentForAPI
*/
getReadTime(propName) {
return this.getReadDataAndTime(propName).readAt;
}
/**
/**
* Access to all the data loaded from the api given the props
* Access to all the data loaded from the api given the props
*
*
...
...
frontend/src/components/ThemeProvider.js
View file @
7266140c
...
@@ -14,6 +14,12 @@ import getActions from "../api/getActions";
...
@@ -14,6 +14,12 @@ import getActions from "../api/getActions";
import
{
saveAppTheme
}
from
"
../actions/theme
"
;
import
{
saveAppTheme
}
from
"
../actions/theme
"
;
/**
* Component that handles theme customization and saving at the scale of the entire app
*
* @class ThemeProvider
* @extends {CustomComponentForAPI}
*/
class
ThemeProvider
extends
CustomComponentForAPI
{
class
ThemeProvider
extends
CustomComponentForAPI
{
state
=
{
theme
:
this
.
props
.
themeSavedInTheApp
.
theme
};
state
=
{
theme
:
this
.
props
.
themeSavedInTheApp
.
theme
};
...
@@ -54,7 +60,10 @@ class ThemeProvider extends CustomComponentForAPI {
...
@@ -54,7 +60,10 @@ class ThemeProvider extends CustomComponentForAPI {
fontSize
:
14
,
fontSize
:
14
,
htmlFontSize
:
14
htmlFontSize
:
14
},
},
useNextVariants
:
true
useNextVariants
:
true
,
myPaper
:
{
padding
:
16
}
};
};
const
theme
=
Object
.
assign
({},
this
.
state
.
theme
,
siteSettings
);
const
theme
=
Object
.
assign
({},
this
.
state
.
theme
,
siteSettings
);
...
...
frontend/src/components/filter/DownshiftMultiple.js
View file @
7266140c
/**
*
* WARNING THIS FILE HAS NOT BEEN REVIEWED AS OF 22.02.2019
* THINGS MIGHT NOT BE SUPER CLEAR OR BROKEN
*
*
*
*
*
*
*
* TODO
*
*
*
*
*/
// Inspired by : https://material-ui.com/demos/autocomplete/
// Inspired by : https://material-ui.com/demos/autocomplete/
import
React
from
"
react
"
;
import
React
from
"
react
"
;
...
@@ -21,9 +42,7 @@ function renderInput(inputProps) {
...
@@ -21,9 +42,7 @@ function renderInput(inputProps) {
<
TextField
<
TextField
InputProps
=
{{
InputProps
=
{{
inputRef
:
ref
,
inputRef
:
ref
,
classes
:
{
classes
:
{
root
:
classes
.
inputRoot
},
root
:
classes
.
inputRoot
,
},