Commit 9caa1a59 authored by Florent Chehab's avatar Florent Chehab

bumped(material-ui version) & Enhance(previous departure front)

* Bumped material-UI to v4.0.0
* Fixed braking changes,
* Moved some components to React Hook along the way
* Fixed some bugs
* v0.4.0 of the frontend image,

---

Enhanced frontend of previous departure, still a WIP
parent 59c8c491
Pipeline #40501 passed with stages
in 4 minutes and 31 seconds
......@@ -34,7 +34,7 @@ check_back:
check_front:
<<: *only-default
stage: check
image: registry.gitlab.utc.fr/rex-dri/rex-dri/frontend:v0.3.0
image: registry.gitlab.utc.fr/rex-dri/rex-dri/frontend:v0.4.0
before_script:
- cd frontend && cp -R /usr/src/deps/node_modules .
script:
......@@ -70,7 +70,7 @@ test_back:
test_frontend:
<<: *only-default
stage: test
image: registry.gitlab.utc.fr/rex-dri/rex-dri/frontend:v0.3.0
image: registry.gitlab.utc.fr/rex-dri/rex-dri/frontend:v0.4.0
before_script:
- cd frontend && cp -R /usr/src/deps/node_modules .
script:
......@@ -90,7 +90,7 @@ flake8:
eslint:
<<: *only-default
stage: lint
image: registry.gitlab.utc.fr/rex-dri/rex-dri/frontend:v0.3.0
image: registry.gitlab.utc.fr/rex-dri/rex-dri/frontend:v0.4.0
before_script:
- cd frontend && cp -R /usr/src/deps/node_modules .
script:
......
......@@ -15,11 +15,11 @@ class CourseFeedback(EssentialModule):
)
comment = models.TextField(null=True, max_length=1500)
adequation = models.IntegerField(
default=0, validators=[MaxValueValidator(5), MinValueValidator(-5)]
default=0, validators=[MinValueValidator(-5), MaxValueValidator(5)]
)
working_dose = models.IntegerField(
default=0, validators=[MaxValueValidator(5), MinValueValidator(-5)]
default=0, validators=[MinValueValidator(-5), MaxValueValidator(5)]
)
language_following_ease = models.IntegerField(
default=0, validators=[MaxValueValidator(5), MinValueValidator(-5)]
default=0, validators=[MinValueValidator(-5), MaxValueValidator(5)]
)
......@@ -23,7 +23,7 @@ class ExchangeFeedback(EssentialModule):
)
general_comment = models.TextField(null=True, max_length=1500)
academical_level_appreciation = models.IntegerField(
validators=[MaxValueValidator(5), MinValueValidator(-5)]
validators=[MinValueValidator(-5), MaxValueValidator(5)]
)
foreign_student_welcome = models.PositiveIntegerField(
validators=[MaxValueValidator(10)]
......
......@@ -63,7 +63,7 @@ services:
# Service to handle frontend live developpments and building
frontend:
# Get the image from the registry
image: registry.gitlab.utc.fr/rex-dri/rex-dri/frontend:v0.3.0
image: registry.gitlab.utc.fr/rex-dri/rex-dri/frontend:v0.4.0
# To use a locally built one, comment above, uncomment bellow.
# build: ./frontend
# On startup, we retrieve the dependencies from the image and start the developpement server
......
This diff is collapsed.
......@@ -20,19 +20,19 @@
"license": "BSD-2-Clause",
"private": true,
"dependencies": {
"@date-io/date-fns": "^1.1.0",
"@date-io/luxon": "^1.1.0",
"@material-ui/core": "^3.9.2",
"@material-ui/icons": "^3.0.2",
"@material-ui/lab": "^3.0.0-alpha.30",
"@date-io/date-fns": "^1.3.6",
"@material-ui/core": "^4.0.0",
"@material-ui/styles": "^4.0.0",
"@material-ui/icons": "^4.0.0",
"@material-ui/lab": "^4.0.0-alpha.13",
"@material-ui/pickers": "^3.0.0",
"axios": "^0.18.0",
"date-fns": "^2.0.0-alpha.25",
"date-fns": "^2.0.0-alpha.27",
"downshift": "^3.2.3",
"fuzzysort": "^1.1.4",
"keycode": "^2.2.0",
"lodash": "^4.17.11",
"mapbox-gl": "^0.54.0",
"material-ui-pickers": "^2.2.1",
"notistack": "^0.4.3",
"react": "^16.8.6",
"react-awesome-slider": "^0.5.2",
......
......@@ -3,7 +3,7 @@
import React from "react";
import PropTypes from "prop-types";
import withStyles from "@material-ui/core/styles/withStyles";
import {withStyles} from "@material-ui/styles";
import FullScreenDialog from "./FullScreenDialog";
import {connect} from "react-redux";
......@@ -85,7 +85,7 @@ const mapDispatchToProps = (dispatch) => {
const styles = theme => ({
content: {
[theme.breakpoints.up("md")]: {
padding: theme.spacing.unit * 3,
padding: theme.spacing(3),
},
[theme.breakpoints.down("sm")]: {
padding: 0,
......
......@@ -83,12 +83,12 @@ const styles = theme => ({
display: "none"
},
[theme.breakpoints.up("lg")]: {
marginLeft: theme.spacing.unit,
marginRight: theme.spacing.unit,
marginLeft: theme.spacing(1),
marginRight: theme.spacing(1),
}
},
mainMenuButtonIcon: {
marginLeft: theme.spacing.unit,
marginLeft: theme.spacing(1),
fontSize: "1.2em",
[theme.breakpoints.down("md")]: {
display: "none"
......@@ -102,8 +102,8 @@ const styles = theme => ({
},
mobileIcon: {},
mobileIconContainer: {
marginLeft: theme.spacing.unit,
marginRight: theme.spacing.unit,
marginLeft: theme.spacing(1),
marginRight: theme.spacing(1),
},
primaryDark: {
backgroundColor: theme.palette.primary.dark,
......
......@@ -13,7 +13,6 @@ import InfoIcon from "@material-ui/icons/Info";
class DrawerMenu extends React.Component {
toListItem(items, inset = false) {
return items.map(({label, route, Icon}, idx) => (
<CustomNavLink key={idx} to={route} onClick={() => this.props.closeDrawer()}>
......@@ -46,7 +45,7 @@ class DrawerMenu extends React.Component {
render() {
return (
<div>
<div style={{zIndex: 200000}}>
<Drawer open={this.props.open} onClose={() => this.props.closeDrawer()}>
<List>{this.toListItem([mainMenuHome])}</List>
<Divider/>
......
......@@ -9,9 +9,9 @@ import { connect } from "react-redux";
* Component to enable the FullScreenDialog to have nice transitions
* @returns
*/
function Transition(props) {
return <Slide direction="up" {...props} />;
}
const Transition = React.forwardRef(function Transition(props, ref) {
return <Slide direction="up" ref={ref} {...props} />;
});
/**
......
import {NavLink} from "react-router-dom";
import React from "react";
import PropTypes from "prop-types";
import {makeStyles} from "@material-ui/styles";
const useStyles = makeStyles(theme => {
return {
link: {
color: theme.palette.text.primary,
textDecoration: "none"
},
};
});
/**
* Renders a NavLink (internal app link) with no decoration
*/
class CustomNavLink extends React.Component {
render() {
const {to, children} = this.props;
return (
<NavLink to={to} style={{textDecoration: "none"}}>
{children}
</NavLink>
);
}
function CustomNavLink(props) {
const {to, children} = props;
return (
<NavLink to={to} className={useStyles().link}>
{children}
</NavLink>
);
}
CustomNavLink.propTypes = {
......
......@@ -79,9 +79,13 @@ const ConnectedErrorBoundary = compose(
* @returns {function(*): function(*): *}
*/
export function withErrorBoundary() {
return Component => setDisplayName("Error boundary")(props => (
<ConnectedErrorBoundary>
<Component {...props}/>
</ConnectedErrorBoundary>
));
return Component => setDisplayName("error-boundary")(
// We need to forward the ref otherwise the styles are not correctly applied.
// eslint-disable-next-line react/display-name
React.forwardRef((props, ref) =>
<ConnectedErrorBoundary>
<Component {...props} ref={ref}/>
</ConnectedErrorBoundary>
)
);
}
......@@ -5,6 +5,12 @@ import Fab from "@material-ui/core/Fab";
import {MenuItem} from "@material-ui/core";
import {NavLink} from "react-router-dom";
/**
* There were errors during migrations to material-ui v4 as Navlink couldn't hold a ref...
*/
// eslint-disable-next-line react/display-name
const ForwardedNavLink = React.forwardRef((props, ref)=> <div ref={ref}><NavLink {...props}/></div>);
class IconWithMenu extends React.Component {
state = {
anchorEl: null,
......@@ -48,7 +54,7 @@ class IconWithMenu extends React.Component {
</MenuItem>
:
<MenuItem key={idx}
component={NavLink}
component={ForwardedNavLink}
to={route}
onClick={this.handleCloseMenu}>
{label}
......@@ -67,7 +73,7 @@ IconWithMenu.defaultProps = {
};
IconWithMenu.propTypes = {
Icon: PropTypes.func.isRequired, // should be a react component
Icon: PropTypes.object.isRequired, // should be a react component
menuItems: PropTypes.array.isRequired, // should be an array of menu items
fabProps: PropTypes.object,
iconProps: PropTypes.object,
......
......@@ -65,7 +65,7 @@ Loading.defaultProps = {
const styles = theme => ({
progress: {
margin: theme.spacing.unit * 2,
margin: theme.spacing(2),
}
});
......
......@@ -2,14 +2,12 @@
/* 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, { Component } from "react";
import React, {Component} from "react";
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";
import Table from "@material-ui/core/Table";
import TableBody from "@material-ui/core/TableBody";
......@@ -18,13 +16,15 @@ import TableHead from "@material-ui/core/TableHead";
import TableRow from "@material-ui/core/TableRow";
import Paper from "@material-ui/core/Paper";
import { lighten, darken } from "@material-ui/core/styles/colorManipulator";
import {darken, lighten} from "@material-ui/core/styles/colorManipulator";
import Divider from "@material-ui/core/Divider";
import TextLink, {getLinkColor} from "../common/TextLink";
import {makeStyles} from "@material-ui/styles";
// Custom styling for the rendered markdown
const styles = theme => {
const { palette } = theme,
// To be used in hooks based components only
const useStyles = makeStyles(theme => {
const {palette} = theme,
backgroundTable = palette.type === "dark" ? lighten(palette.background.paper, 0.07) : darken(palette.background.paper, 0.02),
headerTable = palette.type === "dark" ? lighten(palette.background.paper, 0.13) : darken(palette.background.paper, 0.07);
......@@ -36,7 +36,7 @@ const styles = theme => {
fontFamily: "monospace"
},
listItem: {
marginTop: theme.spacing.unit,
marginTop: theme.spacing(1),
},
blockquote: {
color: palette.text.secondary,
......@@ -64,14 +64,14 @@ const styles = theme => {
fontWeight: 700
}
};
};
});
/////////////////////////////////////////
/// Renderers definition
////////////////////////////////////////
const HeadingRenderer = (props) => {
const { level } = props;
const {level} = props;
let variant = "body2",
paragraph = false;
......@@ -96,59 +96,59 @@ const HeadingRenderer = (props) => {
}
return (
<Typography gutterBottom variant={variant} paragraph={paragraph} >
<Typography gutterBottom variant={variant} paragraph={paragraph}>
{props.children}
</Typography>
);
};
const ListRenderer = (props, classes) => (
<ul className={classes.list}>
const ListRenderer = (props) => (
<ul className={useStyles().list}>
{props.children}
</ul>
);
const ListItemRenderer = (props, classes) => (
<li className={classes.listItem}>
const ListItemRenderer = (props) => (
<li className={useStyles().listItem}>
<Typography component="span">
{props.children}
</Typography >
</Typography>
</li>
);
const CodeRenderer = (props) => (
// className={classes.code} would be override by typography variant
<Typography variant={"body2"} component='code' style={{ fontFamily: "monospace" }}>
<Typography variant={"body2"} component='code' style={{fontFamily: "monospace"}}>
{props.value}
</Typography>
);
const InlineCodeRenderer = (props, classes) => (
<code className={classes.code}>
const InlineCodeRenderer = (props) => (
<code className={useStyles().code}>
{props.children}
</code>
);
const BlockquoteRenderer = (props, classes) => (
<blockquote className={classes.blockquote}>
const BlockquoteRenderer = (props) => (
<blockquote className={useStyles().blockquote}>
<em>
{props.children}
</em>
</blockquote>
);
const TableRenderer = (props, classes) => (
<Paper className={classes.backgroundTable}>
<Table className={classes.table}>{props.children}</Table>
const TableRenderer = (props) => (
<Paper className={useStyles().backgroundTable}>
<Table className={useStyles().table}>{props.children}</Table>
</Paper>
);
const TableHeadRenderer = (props, classes) => (
const TableHeadRenderer = (props) => (
<TableHead
classes={{ root: classes.bold }}
className={classes.tableHead}
classes={{root: useStyles().bold}}
className={useStyles().tableHead}
>
{props.children}
</TableHead>
......@@ -158,23 +158,22 @@ const TableHeadRenderer = (props, classes) => (
const renderers = {
heading: HeadingRenderer,
list: withStyles(styles, { withTheme: true })(ListRenderer),
listItem: withStyles(styles, { withTheme: true })(ListItemRenderer),
paragraph: (props) => <Typography paragraph >{props.children}</Typography>,
list: ListRenderer,
listItem: ListItemRenderer,
paragraph: (props) => <Typography paragraph>{props.children}</Typography>,
link: props => <TextLink {...props} />,
code: withStyles(styles, { withTheme: true })(CodeRenderer),
inlineCode: withStyles(styles, { withTheme: true })(InlineCodeRenderer),
blockquote: withStyles(styles, { withTheme: true })(BlockquoteRenderer),
table: withStyles(styles, { withTheme: true })(TableRenderer),
tableHead: withStyles(styles, { withTheme: true })(TableHeadRenderer),
code: CodeRenderer,
inlineCode: InlineCodeRenderer,
blockquote: BlockquoteRenderer,
table: TableRenderer,
tableHead: TableHeadRenderer,
tableBody: props => (<TableBody>{props.children}</TableBody>),
tableRow: props => (<TableRow hover={true}>{props.children}</TableRow>),
tableCell: props => (<TableCell>{props.children}</TableCell>),
thematicBreak: () => (<Divider />),
thematicBreak: () => (<Divider/>),
};
/**
* Custom Markdown component renderer to make use of material UI
*
......@@ -202,7 +201,7 @@ class Markdown extends Component {
if (!el.isMoney) {
compiled += el.text;
} else {
const { amount, currency } = el;
const {amount, currency} = el;
if (currency === "EUR") {
compiled += `${amount}€`;
......
import React from "react";
import {getGradient, Spectral, viridis} from "../../utils/colormaps";
import PropTypes from "prop-types";
import {classNames} from "../../utils/classNames";
import ThumbsUp from "@material-ui/icons/ThumbUp";
import ThumbsDown from "@material-ui/icons/ThumbDown";
import Close from "@material-ui/icons/Close";
import Warning from "@material-ui/icons/Warning";
import Done from "@material-ui/icons/Done";
import DoneAll from "@material-ui/icons/DoneAll";
import Favorite from "@material-ui/icons/Favorite";
import {makeStyles} from "@material-ui/styles";
const possibleIcons = [Close, Warning, Done, DoneAll, Favorite];
const breakPoints = [20, 40, 60, 80, 100];
const indicatorWidth = 14,
indicatorExtraHeight = 10;
const useStyles = makeStyles(theme => ({
barContainer: {
flexWrap: "nowrap",
display: "flex",
alignItems: "center",
paddingTop: 25,
paddingBottom: 10,
},
barContent: {},
linearBackground: {
background: getGradient([...viridis].reverse())
},
divergentBackground: {
background: getGradient(Spectral)
},
colorBar: {
height: props => props.height,
borderRadius: props => props.height / 2,
marginLeft: 10,
marginRight: 10,
},
indicator: {
height: props => props.height + indicatorExtraHeight,
width: indicatorWidth,
top: -(indicatorExtraHeight / 2),
borderRadius: indicatorExtraHeight,
position: "relative",
background: theme.palette.background.default,
borderStyle: "solid",
borderColor: theme.palette.text.primary,
},
indicatorIcon: {
left: "50%",
transform: "translateX(-50%)",
position: "relative",
top: -29
}
}));
/**
*
* @param {number} percent
* @returns {node}
*/
function getIcon(percent) {
let i = 0;
for (const p of breakPoints) {
if (percent <= p) break;
i += 1;
}
return possibleIcons[i];
}
function MetricFeedback(props) {
const
classes = useStyles(props),
{
min,
max,
value,
width,
showThumbs
} = props,
minValue = parseFloat(min),
maxValue = parseFloat(max),
percent = 100 * Math.abs(minValue - value) / Math.abs(maxValue - minValue),
Icon = getIcon(percent),
colorBarClasses = classNames(
classes.colorBar,
props.type === "linear" ? classes.linearBackground : classes.divergentBackground);
return (
<>
<div className={classes.barContainer}>
{showThumbs ? <ThumbsDown color={"disabled"}/> : <></>}
<div className={classes.barContent}>
<div
className={colorBarClasses}
style={{width}}>
<div className={classes.indicator}
style={{
left: `${percent}%`,
transform: `translateX(-${percent}%)`,
}}>
<Icon color={"secondary"} className={classes.indicatorIcon}/>
</div>
</div>
</div>
{showThumbs ? <ThumbsUp color={"disabled"}/> : <></>}
</div>
</>
);
}
MetricFeedback.propTypes = {
width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
type: PropTypes.oneOf(["divergent", "linear"]).isRequired,
min: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
max: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
height: PropTypes.number.isRequired,
value: PropTypes.number.isRequired,
showThumbs: PropTypes.bool.isRequired,
};
MetricFeedback.defaultProps = {
type: "divergent",
min: "-5",
max: "+5",
value: 0,
width: 150,
height: 20,
showThumbs: false,
};
export default MetricFeedback;
......@@ -93,7 +93,7 @@ const styles = theme => ({
width: "fit-content",
},
wrapper: {
margin: theme.spacing.unit,
margin: theme.spacing(1),
position: "relative",
},
buttonSuccess: {
......
......@@ -10,12 +10,12 @@ import "typeface-roboto";
import getActions from "../../redux/api/getActions";
import {RequestParams} from "../../redux/api/RequestParams";
import {responsiveFontSizes} from "@material-ui/core/styles";
const siteSettings = {
typography: {
fontSize: 14,
htmlFontSize: 14,
useNextVariants: true,
},
myPaper: {
padding: 16
......@@ -35,8 +35,9 @@ export function getTheme(themeData) {
secondary: {main: themeData[type].secondary}
};
const theme = Object.assign({}, siteSettings, {palette});
return createMuiTheme(theme);
const themeObj = Object.assign({}, siteSettings, {palette});
const theme = createMuiTheme(themeObj);
return responsiveFontSizes(theme);
}
/**
......
import React, {useState} from "react";
import Markdown from "./Markdown";
import PropTypes from "prop-types";
import {compose} from "recompose";
import withStyles from "@material-ui/core/styles/withStyles";
import Button from "@material-ui/core/Button";
function TruncatedMarkdown(props) {
const [truncated, setTruncated] = useState(false),
{comment, truncateFromLength, classes} = props,
truncatable = comment.length > truncateFromLength;
return (
<div>
<div className={truncatable && !truncated ? classes.truncated : ""}>
<Markdown source={comment}/>
</div>
{
truncatable ?
<>
{!truncated ? <div className={classes.gradientBorder}/> : <></>}
<Button variant={"contained"}
className={classes.moreButton}
onClick={() => setTruncated(!truncated)}>
{!truncated ? "En lire plus..." : "En lire moins..."}
</Button>
</>
:
<></>
}
</div>
);
}
TruncatedMarkdown.propTypes = {
classes: PropTypes.object.isRequired,
comment: PropTypes.string.isRequired,
truncateFromLength: PropTypes.number.isRequired,
};
TruncatedMarkdown.defaultProps = {
truncateFromLength: 100,
};
const styles = theme => ({
truncated: {
maxHeight: "6rem",
overflow: "hidden",
},
gradientBorder: {
position: "relative",
marginTop: "-3rem",
bottom: 0,