Skip to content

Commit

Permalink
Migrate VM Details to PF4
Browse files Browse the repository at this point in the history
Functional changes:
1. error handling on Create/Edit Disk modal is now handled via field
   level errors. Note that information about resizing disk was moved
   from tooltip to permanent helper text (as per PF4 guidelines)
2. New break-point for responsive resize was added for medium screens.
   PF4 uses bigger fonts so i.e. snapshot card was breaking during
   resizing
3. Overview card in edit mode does not show the OS icon - this saves
   space and prevents layout changes during editing i.e. when error
   message is shown.
  • Loading branch information
rszwajko committed Apr 6, 2022
1 parent a4907a4 commit 581a834
Show file tree
Hide file tree
Showing 34 changed files with 758 additions and 870 deletions.
4 changes: 2 additions & 2 deletions src/components/Grid/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
position: relative;
width: 100%;
min-height: 1px;
padding-right: 20px;
padding-left: 20px;
padding-right: 10px;
padding-left: 10px;

flex: 1 1 0; /* grow shrink basis */
max-width: 100%;
Expand Down
47 changes: 20 additions & 27 deletions src/components/VmDetails/BaseCard.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,16 @@ import {
Badge,
Button,
Card,
CardHeading,
CardHeader,
CardTitle,
CardBody,
CardFooter,
Icon,
noop,
excludeKeys,
} from 'patternfly-react'
} from '@patternfly/react-core'

import style from './style.css'
import CardEditButton from './CardEditButton'
import { Tooltip } from '_/components/tooltips'
import { CheckIcon, TimesIcon } from '@patternfly/react-icons/dist/esm/icons'

/**
* Base VM details card. Support common layouts and view vs edit modes.
Expand Down Expand Up @@ -76,7 +74,7 @@ class BaseCard extends React.Component {
}

renderChildren (childProps) {
const children = this.props.children || noop
const children = this.props.children || (() => {})
return typeof children === 'function'
? children(childProps)
: React.isValidElement(children)
Expand All @@ -87,7 +85,7 @@ class BaseCard extends React.Component {
render () {
const {
title = undefined,
icon = undefined,
icon: TheIcon = undefined,
itemCount = undefined,
editMode = undefined,
editable = true,
Expand All @@ -101,13 +99,18 @@ class BaseCard extends React.Component {
const editing = editMode === undefined ? this.state.edit : editMode
const hasHeading = !!title
const hasBadge = itemCount !== undefined
const hasIcon = icon && icon.type && icon.name

const RenderChildren = this.renderChildren
return (
<Card className={`${style['base-card']} ${className}`} id={`${idPrefix}-card`} {...excludeKeys(this.props, this.propTypeKeys)}>
<Card className={`${style['base-card']} ${className}`} id={`${idPrefix}-card` } isCompact>
{hasHeading && (
<CardHeading className={style['base-card-heading']}>
<CardHeader className={style['base-card-heading']}>

<CardTitle>
{TheIcon && <TheIcon className={style['base-card-title-icon']} />}
{title}
{hasBadge && <Badge isRead>{itemCount}</Badge>}
</CardTitle>
<CardEditButton
tooltip={editTooltip}
editable={editable}
Expand All @@ -117,12 +120,7 @@ class BaseCard extends React.Component {
id={`${idPrefix}-button-edit`}
placement={editTooltipPlacement}
/>
<CardTitle>
{hasIcon && <Icon type={icon.type} name={icon.name} className={style['base-card-title-icon']} />}
{title}
{hasBadge && <Badge className={style['base-card-item-count-badge']}>{itemCount}</Badge>}
</CardTitle>
</CardHeading>
</CardHeader>
)}

<CardBody className={style['base-card-body']}>
Expand All @@ -143,10 +141,8 @@ class BaseCard extends React.Component {

{editing && (
<CardFooter className={style['base-card-footer']}>
<Button disabled={disableSaveButton} bsStyle='primary' onClick={this.clickSave} id={`${idPrefix}-button-save`}>
<Icon type='fa' name='check' />
</Button>
<Button onClick={this.clickCancel} id={`${idPrefix}-button-cancel`}><Icon type='pf' name='close' /></Button>
<Button isDisabled={disableSaveButton} onClick={this.clickSave} id={`${idPrefix}-button-save`} icon={<CheckIcon />}/>
<Button onClick={this.clickCancel} id={`${idPrefix}-button-cancel`} icon={<TimesIcon />} variant='link'/>
</CardFooter>
)}
</Card>
Expand All @@ -155,10 +151,7 @@ class BaseCard extends React.Component {
}
BaseCard.propTypes = {
title: PropTypes.string,
icon: PropTypes.shape({
type: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
}),
icon: PropTypes.any,
itemCount: PropTypes.number,
idPrefix: PropTypes.string,
className: PropTypes.string,
Expand All @@ -176,9 +169,9 @@ BaseCard.propTypes = {
children: PropTypes.oneOfType([PropTypes.func, PropTypes.node]).isRequired,
}
BaseCard.defaultProps = {
onStartEdit: noop,
onCancel: noop,
onSave: noop,
onStartEdit: () => {},
onCancel: () => {},
onSave: () => {},
}

export default BaseCard
14 changes: 6 additions & 8 deletions src/components/VmDetails/CardEditButton.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import React from 'react'
import PropTypes from 'prop-types'
import {
Icon,
noop,
} from 'patternfly-react'

import { PencilAltIcon } from '@patternfly/react-icons/dist/esm/icons'

import style from './style.css'

Expand Down Expand Up @@ -42,7 +40,7 @@ class CardEditButton extends React.Component {
const { editEnabled } = this.state

const classes = `${style['card-edit-button']} ${style[editEnabled ? 'card-edit-button-enabled' : 'card-edit-button-disabled']}`
const myClick = editEnabled ? noop : this.enableEditHandler
const myClick = editEnabled ? () => {} : this.enableEditHandler

if (!editable && disableTooltip) {
return (
Expand All @@ -52,7 +50,7 @@ class CardEditButton extends React.Component {
id={`${id}-card-edit-button-tooltip`}
>
<a className={`${style['card-edit-button']} ${style['card-edit-button-disabled']}`} id={id}>
<Icon type='pf' name='edit' />
<PencilAltIcon/>
</a>
</Tooltip>
)
Expand All @@ -64,7 +62,7 @@ class CardEditButton extends React.Component {
return (
<Tooltip id={`${id}-tooltip`} tooltip={tooltip} placement={placement}>
<a id={id} className={classes} onClick={(e) => { e.preventDefault(); myClick() }}>
<Icon type='pf' name='edit' />
<PencilAltIcon/>
</a>
</Tooltip>
)
Expand All @@ -82,7 +80,7 @@ CardEditButton.propTypes = {
CardEditButton.defaultProps = {
tooltip: '',
editEnabled: false,
onClick: noop,
onClick: () => {},
}

export default CardEditButton
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import React, { useContext } from 'react'
import PropTypes from 'prop-types'
import {
FormControl,
ControlLabel,
FormGroup,
} from 'patternfly-react'
TextArea,
TextInput,
} from '@patternfly/react-core'
import { MsgContext } from '_/intl'

const CloudInitForm = ({ idPrefix, vm, onChange }) => {
Expand All @@ -13,24 +13,25 @@ const CloudInitForm = ({ idPrefix, vm, onChange }) => {
const cloudInitSshAuthorizedKeys = vm.getIn(['cloudInit', 'sshAuthorizedKeys'])
return (
<>
<FormGroup controlId={`${idPrefix}-cloud-init-hostname`}>
<ControlLabel>
{msg.hostName()}
</ControlLabel>
<FormControl
type='text'
<FormGroup
label={msg.hostName()}
fieldId={`${idPrefix}-cloud-init-hostname`}
>
<TextInput
id={`${idPrefix}-cloud-init-hostname`}
type="text"
value={cloudInitHostName}
onChange={e => onChange('cloudInitHostName', e.target.value)}
onChange={value => onChange('cloudInitHostName', value)}
/>
</FormGroup>
<FormGroup controlId={`${idPrefix}-cloud-init-ssh`}>
<ControlLabel>
{msg.sshAuthorizedKeys()}
</ControlLabel>
<FormControl
componentClass='textarea'
<FormGroup
label={msg.sshAuthorizedKeys()}
fieldId={`${idPrefix}-cloud-init-ssh`}
>
<TextArea
id={`${idPrefix}-cloud-init-ssh`}
value={cloudInitSshAuthorizedKeys}
onChange={e => onChange('cloudInitSshAuthorizedKeys', e.target.value)}
onChange={value => onChange('cloudInitSshAuthorizedKeys', value)}
/>
</FormGroup>
</>
Expand Down
63 changes: 32 additions & 31 deletions src/components/VmDetails/cards/DetailsCard/CloudInit/SysprepForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import React, { useContext } from 'react'
import PropTypes from 'prop-types'
import {
Checkbox,
ControlLabel,
FormControl,
FormGroup,
} from 'patternfly-react'
TextArea,
TextInput,
} from '@patternfly/react-core'
import { MsgContext } from '_/intl'
import SelectBox from '../../../../SelectBox'
import timezones from '_/components/utils/timezones.json'
Expand All @@ -19,40 +19,41 @@ const SysprepForm = ({ idPrefix, vm, onChange, lastInitTimezone }) => {

return (
<>
<FormGroup controlId={`${idPrefix}-cloud-init-hostname`}>
<ControlLabel>
{msg.hostName()}
</ControlLabel>
<FormControl
<FormGroup
label={msg.hostName()}
fieldId={`${idPrefix}-sysprep-hostname`}
>
<TextInput
id={`${idPrefix}-sysprep-hostname`}
type='text'
value={cloudInitHostName}
onChange={e => onChange('cloudInitHostName', e.target.value)}
onChange={value => onChange('cloudInitHostName', value)}
/>
</FormGroup>
<FormGroup controlId={`${idPrefix}-cloud-init-hostname`}>
<ControlLabel>
{msg.password()}
</ControlLabel>
<FormControl
<FormGroup
label={msg.password()}
fieldId={`${idPrefix}-sysprep-password`}
>
<TextInput
id={`${idPrefix}-sysprep-password`}
type='password'
value={cloudInitPassword}
onChange={e => onChange('cloudInitPassword', e.target.value)}
onChange={value => onChange('cloudInitPassword', value)}
/>
</FormGroup>

{/* Configure Timezone checkbox */}
<Checkbox
id={`${idPrefix}-sysprep-timezone-config`}
checked={enableInitTimezone}
onChange={e => onChange('enableInitTimezone', e.target.checked)}
>
{msg.sysPrepTimezoneConfigure()}
</Checkbox>
label={msg.sysPrepTimezoneConfigure()}
isChecked={enableInitTimezone}
onChange={checked => onChange('enableInitTimezone', checked)}
/>

<FormGroup controlId={`${idPrefix}-cloud-init-timezone`}>
<ControlLabel>
{msg.timezone()}
</ControlLabel>
<FormGroup
label={msg.timezone()}
fieldId={`${idPrefix}-sysprep-timezone-select`}
>
<SelectBox
id={`${idPrefix}-sysprep-timezone-select`}
items={timezones}
Expand All @@ -61,14 +62,14 @@ const SysprepForm = ({ idPrefix, vm, onChange, lastInitTimezone }) => {
disabled={!enableInitTimezone}
/>
</FormGroup>
<FormGroup controlId={`${idPrefix}-sysprep-custom-script`}>
<ControlLabel>
{msg.customScript()}
</ControlLabel>
<FormControl
componentClass='textarea'
<FormGroup
label={msg.customScript()}
fieldId={`${idPrefix}-sysprep-custom-script`}
>
<TextArea
id={`${idPrefix}-sysprep-custom-script`}
value={cloudInitCustomScript}
onChange={e => onChange('cloudInitCustomScript', e.target.value)}
onChange={value => onChange('cloudInitCustomScript', value)}
/>
</FormGroup>
</>
Expand Down
25 changes: 15 additions & 10 deletions src/components/VmDetails/cards/DetailsCard/FieldRow.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,16 @@ import PropTypes from 'prop-types'

import { Col } from '_/components/Grid'
import { InfoTooltip, Tooltip } from '_/components/tooltips'
import { FormGroup, ControlLabel } from 'patternfly-react'
import { FormGroup } from '@patternfly/react-core'
import classnames from 'classnames'

import style from './style.css'
import gridStyle from '_/components/Grid/style.css'

const FieldRow = ({ label, children, id, tooltip, tooltipPosition, validationState = null }) => (
<FormGroup
className={classnames(gridStyle['grid-row'], style['field-row'])}
validationState={validationState}
>
const FieldRow = ({ label, children, id, tooltip, tooltipPosition, validationState = 'default', useFormGroup }) => (
<div className={classnames(gridStyle['grid-row'], style['field-row'])}>
<Col cols={5} className={style['col-label']}>
<ControlLabel>{label}</ControlLabel>
{label}
{tooltip && (
<span className={style.tooltip}>
<InfoTooltip
Expand All @@ -26,16 +23,24 @@ const FieldRow = ({ label, children, id, tooltip, tooltipPosition, validationSta
</span>
)}
</Col>
<Col cols={7} className={style['col-data']} id={id}>{children}</Col>
</FormGroup>
<Col cols={7} className={style['col-data']} id={id}>
{useFormGroup && (
<FormGroup validated={validationState}>
{children}
</FormGroup>
)}
{!useFormGroup && children}
</Col>
</div>
)
FieldRow.propTypes = {
id: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
tooltip: PropTypes.oneOfType([Tooltip.propTypes.tooltip]),
children: PropTypes.node.isRequired,
tooltipPosition: Tooltip.propTypes.placement,
validationState: FormGroup.propTypes.validationState,
validationState: PropTypes.oneOf(['success', 'warning', 'error', 'default']),
useFormGroup: PropTypes.bool,
}

export default FieldRow
Loading

0 comments on commit 581a834

Please sign in to comment.