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

feat: Add authentification dialog #2469

Merged
merged 5 commits into from
Jul 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions assets/icons/illus/cozy-authentification.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 1 addition & 7 deletions assets/icons/ui/eye-closed.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions docs/styleguide.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ module.exports = {
'../react/NoSsr',
'../react/OutlinedInput',
'../react/Paper',
'../react/PasswordField',
'../react/PieChart',
'../react/Progress',
'../react/ProgressionBanner',
Expand Down
94 changes: 94 additions & 0 deletions react/CozyDialogs/PermissionDialog.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import React from 'react'
import cx from 'classnames'

import { makeStyles } from '../styles'
import CozyTheme from '../CozyTheme'
import ConfirmDialog from './ConfirmDialog'
import PropTypes from 'prop-types'
import Icon from '../Icon'
import Paper from '../Paper'

const useStyles = makeStyles({
floatingIcon: {
top: '-2.25rem',
width: '4.5rem',
height: '4.5rem'
}
})

/**
* Dialog for confirmation actions linked to the Cozy system (permissions, authentication, etc.)
*/
const PermissionDialog = ({
open,
icon,
title,
content,
actions,
actionsLayout,
onClose
}) => {
const styles = useStyles()

return (
<CozyTheme variant="inverted">
<ConfirmDialog
open={open}
JF-Cozy marked this conversation as resolved.
Show resolved Hide resolved
size="small"
disableTitleAutoPadding
classes={{
// remove overflow in makeOverride and replace it by u-ov-visible when https://github.com/cozy/cozy-ui/issues/2284 is solved
paper: 'overflow'
}}
componentsProps={{
dialogTitle: {
className: 'u-ta-center u-pt-2 u-pb-half'
}
}}
title={
JF-Cozy marked this conversation as resolved.
Show resolved Hide resolved
<>
<CozyTheme
variant="normal"
className="u-flex u-flex-justify-center"
>
<Paper
square
elevation={2}
className={cx(
styles.floatingIcon,
'u-pos-absolute u-bdrs-circle u-flex'
)}
>
<Icon className="u-m-auto" icon={icon} size={48} />
</Paper>
</CozyTheme>
{title}
</>
}
content={content}
actions={actions}
actionsLayout={actionsLayout}
onClose={onClose}
/>
</CozyTheme>
)
}

PermissionDialog.propTypes = {
/** To open/close the modal */
open: PropTypes.bool.isRequired,
/** Icon to describe the action to be taken */
icon: PropTypes.func.isRequired,
/** Title of the modal */
title: PropTypes.string.isRequired,
/** Content of the modal */
content: PropTypes.node,
/** Actions of the modal */
actions: PropTypes.node,
/** Actions can be displayed as "rows" or "columns" */
actionsLayout: PropTypes.oneOf(['row', 'column']),
/** Triggered function on modal close action */
onClose: PropTypes.func
}

export default PermissionDialog
17 changes: 12 additions & 5 deletions react/CozyDialogs/Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ import {
ConfirmDialog,
IllustrationDialog,
FixedDialog,
FixedActionsDialog
FixedActionsDialog,
PermissionDialog
} from 'cozy-ui/transpiled/react/CozyDialogs'

import { BreakpointsProvider } from 'cozy-ui/transpiled/react/hooks/useBreakpoints'
Expand All @@ -77,6 +78,7 @@ import FormLabel from 'cozy-ui/transpiled/react/FormLabel'
import BottomSheet, { BottomSheetItem } from 'cozy-ui/transpiled/react/BottomSheet'
import Stack from 'cozy-ui/transpiled/react/Stack'

import ToTheCloudIcon from 'cozy-ui/transpiled/react/Icons/ToTheCloud'
import CloudIcon from "cozy-ui/transpiled/react/Icons/Cloud"
import BackgroundImg from './background.png'

Expand Down Expand Up @@ -133,31 +135,35 @@ const dialogTitles = {
IllustrationDialog: <Icon icon={CloudIcon} size="140" />,
FixedDialog: 'Fixed Dialog',
FixedActionsDialog: 'Fixed Actions Dialog',
Dialog: 'Dialog'
Dialog: 'Dialog',
PermissionDialog: 'Are you sure ?'
}

const dialogContents = {
ConfirmDialog: "Content of a confirm dialog, precising what the actions will do, and asking the user if she is sure.",
IllustrationDialog: "An IllustrationDialog contains short content." + content.ada.short,
FixedDialog: "A FixedDialog can contain very long content. Actions are at the bottom of the content are not visible to the user if she has not scrolled to the bottom. " + content.ada.long,
FixedActionsDialog: "A FixedActionsDialog can contain very long content. Actions are visible even without scrolling. " + content.ada.long,
Dialog: "A normal Dialog should contain short content. " + content.ada.short
Dialog: "A normal Dialog should contain short content. " + content.ada.short,
PermissionDialog: "Content of a confirm dialog, precising what the actions will do, and asking the user if she is sure.",
}

const dialogActions = {
ConfirmDialog: <ConfirmDialogActions />,
IllustrationDialog: <ExampleDialogActions />,
FixedDialog: <ExampleDialogActions />,
FixedActionsDialog: <ExampleDialogActions />,
Dialog: <ExampleDialogActions />
Dialog: <ExampleDialogActions />,
PermissionDialog: <ExampleDialogActions />,
}

const dialogs = [
Dialog,
ConfirmDialog,
IllustrationDialog,
FixedDialog,
FixedActionsDialog
FixedActionsDialog,
PermissionDialog
]

const StateRadio = ({ name, ...props }) => {
Expand Down Expand Up @@ -326,6 +332,7 @@ const setFlagshipVars = () => {
}
disableGutters={variant.disableGutters}
background={variant.withBackground ? `var(--paperBackgroundColor) repeat-x url(${BackgroundImg})` : undefined}
icon={DialogComponent === PermissionDialog ? CloudIcon : undefined}
content={
<>
<Typography component="div" variant="body1">
Expand Down
109 changes: 109 additions & 0 deletions react/CozyDialogs/SpecificDialogs/AuthentificationDialog.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import React, { useMemo, useState } from 'react'
import PropTypes from 'prop-types'

import PermissionDialog from '../PermissionDialog'
import Buttons from '../../Buttons'
import { useI18n } from '../../I18n'
import Typography from '../../Typography'
import { useClient } from 'cozy-client'
import CozyAuthentificationIcon from '../../Icons/CozyAuthentification'
import PasswordField from '../../PasswordField'

import withSpecificDialogsLocales from './withSpecificDialogsLocales'

/**
* Dialog used to authenticate a user in the cozy system.
* The authentication logic is implemented in the applications.
*/
const AuthentificationDialog = ({
onClose,
onSubmit,
isLoading,
isOIDC,
error
}) => {
const { t } = useI18n()
const client = useClient()
const [password, setPassword] = useState('')

const handleSubmit = e => {
e.preventDefault()
onSubmit(password)
}

const onPasswordChange = e => {
setPassword(e.currentTarget.value)
}

const passphraseResetUrl = useMemo(() => {
const url = new URL('/auth/passphrase_reset', client.getStackClient().uri)
return url.href
}, [client])

return (
<PermissionDialog
open
onClose={onClose}
title={t(`authentification-dialog.${isOIDC ? 'title-oidc' : 'title'}`)}
icon={CozyAuthentificationIcon}
content={
<form onSubmit={handleSubmit}>
<Typography variant="body1" className="u-ta-center">
{t('authentification-dialog.subtitle')}
</Typography>
<PasswordField
autoFocus
disabled={isLoading}
value={password}
onChange={onPasswordChange}
className="u-mv-1"
label={t(
`authentification-dialog.${isOIDC ? 'label-oidc' : 'label'}`
)}
error={Boolean(error)}
helperText={error && t(`authentification-dialog.errors.${error}`)}
fullWidth
required
/>
<Typography
variant="body1"
component="a"
color="primary"
href={passphraseResetUrl}
className="u-link"
>
{t('authentification-dialog.forgotten-password')}
</Typography>
</form>
}
actions={
<Buttons
busy={isLoading}
disabled={isLoading || password.length === 0}
onClick={handleSubmit}
label={t('authentification-dialog.unlock')}
fullWidth
/>
}
/>
)
}

AuthentificationDialog.defaultProps = {
isOIDC: false
}

AuthentificationDialog.propTypes = {
/** A function call on clicking the close button */
onClose: PropTypes.func,
/** A function call on submitting the form with the password entered */
onSubmit: PropTypes.func,
/** Waiting status, e.g. processing of form submission */
isLoading: PropTypes.bool,
/** Show specific wording for OIDC */
isOIDC: PropTypes.bool,
JF-Cozy marked this conversation as resolved.
Show resolved Hide resolved
/** Error key to display a message */
error: PropTypes.string
}

export default withSpecificDialogsLocales(AuthentificationDialog)
58 changes: 58 additions & 0 deletions react/CozyDialogs/SpecificDialogs/Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,61 @@ const [open, setOpen] = useState(isTesting());
<Buttons onClick={() => { setOpen(true) }} label="Open InstallFlagshipAppDialog" />
</DemoProvider>
```

### Authentification dialog

Dialog used to authenticate a user in the cozy system. The authentication logic is implemented in the applications.

```jsx
import { AuthentificationDialog } from 'cozy-ui/transpiled/react/CozyDialogs'
import Button from 'cozy-ui/transpiled/react/Buttons'
import DemoProvider from 'cozy-ui/docs/components/DemoProvider'
import Variants from 'cozy-ui/docs/components/Variants'
import FormControlLabel from 'cozy-ui/transpiled/react/FormControlLabel'
import RadioGroup from 'cozy-ui/transpiled/react/RadioGroup'
import Radio from 'cozy-ui/transpiled/react/Radios'
import FormControl from 'cozy-ui/transpiled/react/FormControl'
import FormLabel from 'cozy-ui/transpiled/react/FormLabel'

initialState = { showModal: false };

const initialVariants = [{
closable: true,
loading: false,
oidc: false
}]

const initialErrors = [{
default: true,
invalid_password: false,
server_error: false
}]

const onClose = () => setState({ showModal: false })

const onAuthentification = () => {
alert('authentification')
setState({ showModal: false })
};

<Variants initialVariants={initialVariants}>
{variant => (
<Variants initialVariants={initialErrors} radio>
{error => (
<DemoProvider>
<Button label="Open modal" onClick={() => setState({ showModal: true })}/>
{state.showModal && (
<AuthentificationDialog
onSubmit={onAuthentification}
onClose={variant.closable && onClose}
error={error.invalid_password ? 'invalid_password' : error.server_error ? 'server_error' : undefined }
isLoading={variant.loading}
isOIDC={variant.oidc}
/>
)}
</DemoProvider>
)}
</Variants>
)}
</Variants>
```
1 change: 1 addition & 0 deletions react/CozyDialogs/SpecificDialogs/index.jsx
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { default as InstallFlagshipAppDialog } from './InstallFlagshipAppDialog'
export { default as AuthentificationDialog } from './AuthentificationDialog'
14 changes: 14 additions & 0 deletions react/CozyDialogs/SpecificDialogs/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,19 @@
"title": "Scan the QR Code",
"text": "or go directly to the <img src=%{iosIconSrc} /> <a href='%{iosUrl}' class='u-link' target='_blank' rel='noopener'>App Store</a><br>or <img src=%{androidIconSrc} /> <a href='%{androidUrl}' class='u-link' target='_blank' rel='noopener'>Play Store</a> to install the Cozy Cloud app",
"a11n": "Go to the Cozy Cloud application download page"
},
"authentification-dialog": {
"title": "Login",
"title-oidc": "Cozy Pass",
"subtitle": "For security, please confirm your identity",
"label": "Cozy password",
"label-oidc": "Cozy Pass password",
"abort": "Leave",
"unlock": "Unlock",
"forgotten-password": "I forgot my password",
"errors": {
"invalid_password": "Incorrect password, try again.",
"server_error": "Something went wrong with the server. Please, reload the page."
}
}
}
14 changes: 14 additions & 0 deletions react/CozyDialogs/SpecificDialogs/locales/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,19 @@
"title": "Scannez le QR Code",
"text": "ou rendez vous directement sur <img src=%{iosIconSrc} /> <a href='%{iosUrl}' class='u-link' target='_blank' rel='noopener'>l’App Store</a><br>ou sur le <img src=%{androidIconSrc} /> <a href='%{androidUrl}' class='u-link' target='_blank' rel='noopener'>Play Store</a> pour installer l’app Cloud Personnel Cozy",
"a11n": "Aller sur la page de téléchargement de l'application Cloud Personnel Cozy"
},
"authentification-dialog": {
"title": "Authentification",
"title-oidc": "Cozy Pass",
"subtitle": "Par sécurité, merci de confirmer votre identité",
"label": "Mot de passe Cozy",
"label-oidc": "Mot de passe Cozy Pass",
"abort": "Quitter",
"unlock": "Déverrouiller",
"forgotten-password": "J'ai oublié mon mot de passe",
"errors": {
"invalid_password": "Mot de passe incorrect, essayer à nouveau.",
"server_error": "Une erreur s'est produite. Merci de recharger la page."
}
}
}
Loading