diff --git a/doc/adrs/004_migrate_our_components_to_materialui.adoc b/doc/adrs/004_migrate_our_components_to_materialui.adoc new file mode 100644 index 0000000000..bfba544590 --- /dev/null +++ b/doc/adrs/004_migrate_our_components_to_materialui.adoc @@ -0,0 +1,106 @@ += ADR-004 - Migrate our components to Material UI + +== Context + +We have started the project with our own custom made components. +Is has helped us not get overwhelmed with frameworks and it has also been an opporunity for new members of our team to start working on simple tasks. +We are now trying to create complex pieces of user interface and doing so all by ourselves is distracting us from the core issues that we are trying to tackle. + +MaterialUI is a great UI framework with a good community and tons of features. + +== Decision + +We have decided to move our user interface to an off the shelves user interface component library, MaterialUI. + +== Status + +Accepted. + +== Consequences + +While this may not be the perfect place to do so, we will list here some consequences of this migration such as the new patterns that we will use. + +=== Theme and style + +Sirius Components will use various components from MaterialUI but it will not define the theme to use. +Applications based on top of Sirius Components will have to define a theme. +Since Sirius Components will have to handle a large number of use cases, it may add some constraints on the required theme. +Such constraints may even go as far as providing some core theme settings. + +=== CSS in JS + +In order to align our code with the best practices from the MaterialUI community, our CSS will move progressively from CSS modules to CSS in JS. +Thanks to CSS in JS, we will be able to compute CSS dynamically from the theme. + +The theme will be responsible for the definition of colors, text styles, spacing, etc. +As such, we will not define anymore in the CSS of Sirius Components rules regarding: + +- Text-related properties font, font size, weight, line height +- Custom colors such as `rgb(x, y, z)` or `#ABCDEF` +- Custom spacing such as `padding-top: 16px` + +To create our CSS, we will rely on the function `makeStyles`. + +``` +import { makeStyles } from '@material-ui/core/styles'; + +const useFormStyles = makeStyles((theme) => ({ + form: { + display: 'flex', + flexDirection: 'column', + }, +})); + +export const Form = ({ children }) => { + const classes = useFormStyles(); + return ( +
+ {children} +
+ ); +}; +``` + +=== Text styling + +In order to display some text in the user interface, we will rely on the `` component. +To specify the look of this text, we will use the `variant` property, we will always define a variant when using typography. +In order to display some text, we will thus use something like this: + +``` +Hello World +``` + +We will rely on standard variants of MaterialUI and only introduce custom ones if we are absolutely stuck. +Such change would need to be properly documented since it would introduce a custom "design API" that our users would have to follow. + +=== Layout + +Most of our layout will continue to leverage custom usage of flexbox and grid on regular divs. +Try not to create grids if you only have one column or one row since the grid layout has a greater performance impact than the flexbox layout. + +In order to fine tune some internal details of our layout, we will continue to rely mostly on `padding` (since `margin` is creating more issues in order to reuse some components). +We will reuse the theme in order to compute sizes for the padding. + +``` +const useViewStyles = makeStyles((theme) => ({ + main: { + paddingTop: theme.spacing(3), + paddingBottom: theme.spacing(3), + }, +})); + +export const View = ({ children }) => { + const classes = useViewStyles(); + + return ( +
+ {children} +
+ ); +}; +``` + +== References + +- https://material-ui.com \ No newline at end of file diff --git a/frontend/src/core/banner/Banner.tsx b/frontend/src/core/banner/Banner.tsx index d037a292b4..43c368aea3 100644 --- a/frontend/src/core/banner/Banner.tsx +++ b/frontend/src/core/banner/Banner.tsx @@ -9,6 +9,9 @@ * * Contributors: * Obeo - initial API and implementation + * + * @deprecated use @material-ui/core/Snackbar instead + * *******************************************************************************/ import { Text } from 'core/text/Text'; import { Danger } from 'icons'; diff --git a/frontend/src/core/form/Form.module.css b/frontend/src/core/form/Form.module.css deleted file mode 100644 index 47ca948055..0000000000 --- a/frontend/src/core/form/Form.module.css +++ /dev/null @@ -1,23 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2019, 2020 Obeo. - * This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v2.0 - * which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * Obeo - initial API and implementation - *******************************************************************************/ -.form { - display: flex; - flex-direction: column; -} - -.form > * { - margin-bottom: 24px; -} -.form > *:last-child { - margin-bottom: 0px; -} diff --git a/frontend/src/core/form/Form.tsx b/frontend/src/core/form/Form.tsx index 0a661ef68b..30b64d6909 100644 --- a/frontend/src/core/form/Form.tsx +++ b/frontend/src/core/form/Form.tsx @@ -10,25 +10,32 @@ * Contributors: * Obeo - initial API and implementation *******************************************************************************/ -import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; import PropTypes from 'prop-types'; +import React from 'react'; -import styles from './Form.module.css'; +const useFormStyles = makeStyles((theme) => ({ + form: { + display: 'flex', + flexDirection: 'column', + paddingTop: theme.spacing(1), + paddingLeft: theme.spacing(2), + paddingRight: theme.spacing(2), + '& > *': { + marginBottom: theme.spacing(2), + }, + }, +})); const propTypes = { children: PropTypes.node.isRequired, - onSubmit: PropTypes.func.isRequired, - encType: PropTypes.string, }; -export const Form = ({ children, onSubmit, encType }) => { - const props = { - className: styles.form, - onSubmit, - encType: undefined, - }; - if (encType) { - props.encType = encType; - } - return
{children}
; +export const Form = ({ children, ...props }) => { + const classes = useFormStyles(); + return ( +
+ {children} +
+ ); }; Form.propTypes = propTypes; diff --git a/frontend/src/views/FormContainer.module.css b/frontend/src/views/FormContainer.module.css deleted file mode 100644 index 3f2218bd24..0000000000 --- a/frontend/src/views/FormContainer.module.css +++ /dev/null @@ -1,49 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2019, 2020 Obeo. - * This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v2.0 - * which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * Obeo - initial API and implementation - *******************************************************************************/ -.container { - display: grid; - grid-template-columns: 1fr; - grid-template-rows: min-content 80px min-content; -} - -.bannerArea { - display: grid; - grid-template-rows: 1fr; - grid-template-columns: 1fr; - align-items: center; -} - -.content { - display: grid; - grid-template-rows: 1fr; - grid-template-columns: 1fr; -} - -.titleContainer { - display: grid; - grid-template-rows: repeat(2, min-content); - justify-items: center; - - row-gap: 16px; -} - -.title { - font-size: var(--font-size-1); - font-weight: var(--font-weight-bold); - color: var(--daintree); -} - -.subtitle { - font-size: var(--font-size-5); - color: var(--daintree-lighten-30); -} diff --git a/frontend/src/views/FormContainer.tsx b/frontend/src/views/FormContainer.tsx index 9edf08befd..a51c911041 100644 --- a/frontend/src/views/FormContainer.tsx +++ b/frontend/src/views/FormContainer.tsx @@ -10,29 +10,49 @@ * Contributors: * Obeo - initial API and implementation *******************************************************************************/ -import { Banner } from 'core/banner/Banner'; -import { Text } from 'core/text/Text'; +import Paper from '@material-ui/core/Paper'; +import { makeStyles } from '@material-ui/core/styles'; +import Typography from '@material-ui/core/Typography'; import PropTypes from 'prop-types'; import React from 'react'; -import styles from './FormContainer.module.css'; + +const useFormContainerStyles = makeStyles((theme) => ({ + formContainer: { + display: 'flex', + flexDirection: 'column', + paddingTop: theme.spacing(8), + }, + content: { + display: 'grid', + gridTemplateRows: '1fr', + gridTemplateColumns: '1fr', + }, + titleContainer: { + display: 'flex', + flexDirection: 'column', + paddingBottom: theme.spacing(2), + }, +})); const propTypes = { title: PropTypes.string.isRequired, subtitle: PropTypes.string.isRequired, - banner: PropTypes.node, children: PropTypes.node.isRequired, }; +export const FormContainer = ({ title, subtitle, children }) => { + const classes = useFormContainerStyles(); -export const FormContainer = ({ title, subtitle, banner, children }) => { - let bannerContent = banner ? : null; return ( -
-
- {title} - {subtitle} +
+
+ + {title} + + + {subtitle} +
-
{bannerContent}
-
{children}
+ {children}
); }; diff --git a/frontend/src/views/View.module.css b/frontend/src/views/View.module.css deleted file mode 100644 index 0344fcd974..0000000000 --- a/frontend/src/views/View.module.css +++ /dev/null @@ -1,42 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2019, 2020 Obeo. - * This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v2.0 - * which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * Obeo - initial API and implementation - *******************************************************************************/ -.view { - display: grid; - grid-template-columns: 1fr; - grid-template-rows: min-content 1fr min-content; - min-height: 100vh; -} - -.container { - display: grid; - grid-template-columns: 1fr var(--max-width) 1fr; - grid-template-rows: 64px 1fr; - - --max-width: 960px; -} - -.condensed { - --max-width: 560px; -} - -.content { - display: grid; - grid-template-columns: 1fr; - grid-template-rows: 1fr; - - grid-column-start: 2; - grid-column-end: 3; - - grid-row-start: 2; - grid-row-end: 3; -} diff --git a/frontend/src/views/View.tsx b/frontend/src/views/View.tsx index 7326c3d3e8..16255304df 100644 --- a/frontend/src/views/View.tsx +++ b/frontend/src/views/View.tsx @@ -1,4 +1,3 @@ -import { useBranding } from 'common/BrandingContext'; /******************************************************************************* * Copyright (c) 2019, 2020 Obeo. * This program and the accompanying materials @@ -11,29 +10,41 @@ import { useBranding } from 'common/BrandingContext'; * Contributors: * Obeo - initial API and implementation *******************************************************************************/ -//import { Navbar } from 'navbar/Navbar'; +import Container from '@material-ui/core/Container'; +import { makeStyles } from '@material-ui/core/styles'; +import { useBranding } from 'common/BrandingContext'; import { Navbar } from 'navbar/Navbar'; import PropTypes from 'prop-types'; import React from 'react'; -import styles from './View.module.css'; + +const useViewStyles = makeStyles((theme) => ({ + view: { + display: 'grid', + gridTemplateColumns: '1fr', + gridTemplateRows: 'min-content 1fr min-content', + minHeight: '100vh', + }, + main: { + paddingTop: theme.spacing(3), + paddingBottom: theme.spacing(3), + }, +})); const propTypes = { children: PropTypes.node, condensed: PropTypes.bool, }; - export const View = ({ children, condensed }) => { + const classes = useViewStyles(); + const { footer } = useBranding(); - let containerClassName = styles.container; - if (condensed) { - containerClassName = `${containerClassName} ${styles.condensed}`; - } + const maxWidth = condensed ? 'sm' : 'xl'; return ( -
+
-
-
{children}
-
+
+ {children} +
{footer}
); diff --git a/frontend/src/views/new-project/NewProjectView.tsx b/frontend/src/views/new-project/NewProjectView.tsx index 09c27a9a3b..3fa1f9036b 100644 --- a/frontend/src/views/new-project/NewProjectView.tsx +++ b/frontend/src/views/new-project/NewProjectView.tsx @@ -23,8 +23,8 @@ import { FormContainer } from 'views/FormContainer'; import { View } from 'views/View'; import { HANDLE_CHANGE_NAME__ACTION, - HANDLE_SUBMIT__ACTION, HANDLE_SUBMIT_FAILURE__ACTION, + HANDLE_SUBMIT__ACTION, VALID__STATE, } from './machine'; import styles from './NewProjectView.module.css'; @@ -62,7 +62,7 @@ export const NewProjectView = () => { const [createProject, { loading, data, error }] = useMutation(createProjectMutation); const [state, dispatch] = useReducer(reducer, initialState); - const { viewState, name, nameMessage, nameMessageSeverity, message, newProjectId } = state; + const { viewState, name, nameMessage, nameMessageSeverity, newProjectId } = state; const onNameChange = (event) => { const name = event.target.value; @@ -101,7 +101,7 @@ export const NewProjectView = () => { ) : null; return ( - +