1
0
mirror of https://github.com/OpenNebula/one.git synced 2025-03-21 14:50:08 +03:00

F #5422: Add accordion wrapper to form sections (#2058)

This commit is contained in:
Sergio Betanzos 2022-05-19 13:01:55 +02:00 committed by GitHub
parent 3ff275e1b7
commit a3307765e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 134 additions and 93 deletions

View File

@ -144,7 +144,7 @@ const NicCard = memo(
const rulesById = Object.entries(groupBy(SECURITY_GROUPS, 'ID'))
return (
<Accordion>
<Accordion variant="outlined">
<AccordionSummary>
<Typography variant="body1">
<Translate word={T.SecurityGroups} />

View File

@ -14,11 +14,17 @@
* limitations under the License. *
* ------------------------------------------------------------------------- */
/* eslint-disable jsdoc/require-jsdoc */
import { createElement, useMemo, isValidElement } from 'react'
import {
Fragment,
createElement,
useMemo,
useCallback,
isValidElement,
} from 'react'
import PropTypes from 'prop-types'
import { FormControl, Grid } from '@mui/material'
import { useFormContext } from 'react-hook-form'
import { FormControl, Accordion, AccordionSummary, Grid } from '@mui/material'
import * as FC from 'client/components/FormControl'
import Legend from 'client/components/Forms/Legend'
@ -46,6 +52,7 @@ const INPUT_CONTROLLER = {
}
const FormWithSchema = ({
accordion = false,
id,
cy,
fields,
@ -59,6 +66,26 @@ const FormWithSchema = ({
const { sx: sxRoot, ...restOfRootProps } = rootProps ?? {}
const RootWrapper = useMemo(
() =>
accordion && legend
? ({ children }) => (
<Accordion
variant="transparent"
TransitionProps={{ unmountOnExit: false }}
>
{children}
</Accordion>
)
: Fragment,
[accordion, legend]
)
const LegendWrapper = useMemo(
() => (accordion && legend ? AccordionSummary : Fragment),
[accordion, legend]
)
const getFields = useMemo(
() => (typeof fields === 'function' ? fields() : fields),
[fields?.length]
@ -66,12 +93,15 @@ const FormWithSchema = ({
if (!getFields || getFields?.length === 0) return null
const addIdToName = (name) =>
name.startsWith('$')
? name.slice(1) // removes character '$' and returns
: id
? `${id}.${name}`
: name // concat form ID if exists
const addIdToName = useCallback(
(name) =>
name.startsWith('$')
? name.slice(1) // removes character '$' and returns
: id
? `${id}.${name}` // concat form ID if exists
: name,
[id]
)
return (
<FormControl
@ -80,65 +110,75 @@ const FormWithSchema = ({
sx={{ width: '100%', ...sxRoot }}
{...restOfRootProps}
>
{legend && <Legend title={legend} tooltip={legendTooltip} />}
<Grid container spacing={1} alignContent="flex-start">
{getFields?.map?.(({ dependOf, ...attributes }) => {
let valueOfDependField = null
let nameOfDependField = null
<RootWrapper>
<LegendWrapper>
{legend && (
<Legend
title={legend}
tooltip={legendTooltip}
disableGutters={accordion}
/>
)}
</LegendWrapper>
<Grid container spacing={1} alignContent="flex-start">
{getFields?.map?.(({ dependOf, ...attributes }) => {
let valueOfDependField = null
let nameOfDependField = null
if (dependOf) {
nameOfDependField = Array.isArray(dependOf)
? dependOf.map(addIdToName)
: addIdToName(dependOf)
if (dependOf) {
nameOfDependField = Array.isArray(dependOf)
? dependOf.map(addIdToName)
: addIdToName(dependOf)
valueOfDependField = watch(nameOfDependField)
}
valueOfDependField = watch(nameOfDependField)
}
const { name, type, htmlType, grid, ...fieldProps } = Object.entries(
attributes
).reduce((field, attribute) => {
const [key, value] = attribute
const isNotDependAttribute = NOT_DEPEND_ATTRIBUTES.includes(key)
const { name, type, htmlType, grid, ...fieldProps } =
Object.entries(attributes).reduce((field, attribute) => {
const [key, value] = attribute
const isNotDependAttribute = NOT_DEPEND_ATTRIBUTES.includes(key)
const finalValue =
typeof value === 'function' &&
!isNotDependAttribute &&
!isValidElement(value())
? value(valueOfDependField, formContext)
: value
const finalValue =
typeof value === 'function' &&
!isNotDependAttribute &&
!isValidElement(value())
? value(valueOfDependField, formContext)
: value
return { ...field, [key]: finalValue }
}, {})
return { ...field, [key]: finalValue }
}, {})
const dataCy = `${cy}-${name}`.replaceAll('.', '-')
const inputName = addIdToName(name)
const dataCy = `${cy}-${name}`.replaceAll('.', '-')
const inputName = addIdToName(name)
const isHidden = htmlType === INPUT_TYPES.HIDDEN
const isHidden = htmlType === INPUT_TYPES.HIDDEN
if (isHidden) return null
if (isHidden) return null
return (
INPUT_CONTROLLER[type] && (
<Grid key={dataCy} item xs={12} md={6} {...grid}>
{createElement(INPUT_CONTROLLER[type], {
control,
cy: dataCy,
formContext,
dependencies: nameOfDependField,
name: inputName,
type: htmlType === false ? undefined : htmlType,
...fieldProps,
})}
</Grid>
return (
INPUT_CONTROLLER[type] && (
<Grid key={dataCy} item xs={12} md={6} {...grid}>
{createElement(INPUT_CONTROLLER[type], {
control,
cy: dataCy,
formContext,
dependencies: nameOfDependField,
name: inputName,
type: htmlType === false ? undefined : htmlType,
...fieldProps,
})}
</Grid>
)
)
)
})}
</Grid>
})}
</Grid>
</RootWrapper>
</FormControl>
)
}
FormWithSchema.propTypes = {
accordion: PropTypes.bool,
id: PropTypes.string,
cy: PropTypes.string,
fields: PropTypes.oneOfType([

View File

@ -18,24 +18,30 @@ import PropTypes from 'prop-types'
import { styled, Typography } from '@mui/material'
import AdornmentWithTooltip from 'client/components/FormControl/Tooltip'
import { Translate, labelCanBeTranslated } from 'client/components/HOC'
import { Translate } from 'client/components/HOC'
const StyledLegend = styled((props) => (
<Typography variant="subtitle1" component="legend" {...props} />
))(({ theme, tooltip }) => ({
marginBottom: '1em',
padding: '0em 1em 0.2em 0.5em',
borderBottom: `2px solid ${theme.palette.secondary.main}`,
...(!!tooltip && {
display: 'inline-flex',
alignItems: 'center',
))(
({ theme }) => ({
padding: '0em 1em 0.2em 0.5em',
borderBottom: `2px solid ${theme.palette.secondary.main}`,
}),
}))
({ ownerState }) => ({
...(ownerState.tooltip && {
display: 'inline-flex',
alignItems: 'center',
}),
...(!ownerState.disableGutters && {
marginBottom: '1em',
}),
})
)
const Legend = memo(
({ title, tooltip }) => (
<StyledLegend tooltip={tooltip}>
{labelCanBeTranslated(title) ? <Translate word={title} /> : title}
({ title, tooltip, disableGutters }) => (
<StyledLegend ownerState={{ tooltip, disableGutters }}>
<Translate word={title} />
{!!tooltip && <AdornmentWithTooltip title={tooltip} />}
</StyledLegend>
),
@ -45,6 +51,7 @@ const Legend = memo(
Legend.propTypes = {
title: PropTypes.any,
tooltip: PropTypes.string,
disableGutters: PropTypes.bool,
}
Legend.displayName = 'FieldsetLegend'

View File

@ -64,12 +64,17 @@ const ConfigurationSection = () => {
<Stack direction="row" gap="1em">
<Button
onClick={handleAddUserKey}
color="secondary"
variant="contained"
data-cy={`${EXTRA_ID}-add-context-ssh-public-key`}
>
{T.AddUserSshPublicKey}
</Button>
<Button onClick={handleClearKey} variant="outlined">
<Button
onClick={handleClearKey}
color="secondary"
variant="outlined"
>
{T.Clear}
</Button>
</Stack>

View File

@ -31,6 +31,7 @@ export const SECTION_ID = 'CONTEXT'
*/
const FilesSection = ({ hypervisor }) => (
<FormWithSchema
accordion
cy={`${EXTRA_ID}-context-files`}
legend={T.Files}
fields={() => FILES_FIELDS(hypervisor)}

View File

@ -30,8 +30,8 @@ export const TAB_ID = ['CONTEXT', USER_INPUTS_ID]
const Context = (props) => (
<>
<ConfigurationSection />
<FilesSection {...props} />
<UserInputsSection />
<FilesSection {...props} />
</>
)

View File

@ -116,7 +116,7 @@ const AttributeList = ({
return (
<RootElement variant="outlined" {...containerProps}>
<ListElement {...listProps}>
<ListElement variant="outlined" {...listProps}>
{/* TITLE */}
{title && (
<TitleElement>

View File

@ -43,7 +43,7 @@ const AppTemplateTab = ({ id }) => {
return (
<>
<Accordion>
<Accordion variant="outlined">
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Translate word={T.AppTemplate} />
</AccordionSummary>

View File

@ -34,7 +34,7 @@ const VmConfigurationTab = ({ id }) => {
return (
<div>
<Accordion>
<Accordion variant="outlined">
<AccordionSummary>
<Translate word={T.UserTemplate} />
</AccordionSummary>
@ -46,7 +46,7 @@ const VmConfigurationTab = ({ id }) => {
</pre>
</AccordionDetails>
</Accordion>
<Accordion>
<Accordion variant="outlined">
<AccordionSummary>
<Translate word={T.Template} />
</AccordionSummary>

View File

@ -30,7 +30,7 @@ const TemplateTab = ({ id }) => {
const { data: vmTemplate = {} } = useGetTemplateQuery({ id })
return (
<Accordion expanded>
<Accordion variant="outlined" expanded>
<AccordionDetails>
<Box component="pre">
<Box

View File

@ -287,9 +287,18 @@ const createAppTheme = (appTheme, mode = SCHEMES.DARK) => {
},
},
MuiPaper: {
defaultProps: {
elevation: 0,
},
styleOverrides: {
root: { backgroundImage: 'unset' },
},
variants: [
{
props: { variant: 'transparent' },
style: { backgroundColor: 'transparent' },
},
],
},
MuiButtonBase: {
defaultProps: {
@ -461,39 +470,18 @@ const createAppTheme = (appTheme, mode = SCHEMES.DARK) => {
dense: true,
},
},
MuiChip: {
variants: [
{
props: { variant: 'text' },
style: {
border: 0,
backgroundColor: 'transparent',
},
},
],
},
MuiAccordion: {
defaultProps: {
elevation: 0,
square: true,
disableGutters: true,
TransitionProps: { unmountOnExit: true },
},
styleOverrides: {
root: {
flexBasis: '100%',
border: `1px solid ${defaultTheme.palette.divider}`,
'&:before': { display: 'none' },
},
},
},
MuiAccordionDetails: {
styleOverrides: {
root: {
borderTop: `1px solid ${defaultTheme.palette.divider}`,
},
},
},
MuiAccordionSummary: {
defaultProps: {
expandIcon: <ExpandMoreIcon />,