Skip to content

Commit

Permalink
Migrate Account Settings to PF4
Browse files Browse the repository at this point in the history
Includes:
1. new SelectBox
2. reactoring to link fieldId with label provided by FormGroup
  • Loading branch information
rszwajko committed Dec 7, 2021
1 parent a3f9301 commit 2fec83e
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 186 deletions.
84 changes: 34 additions & 50 deletions src/components/SelectBox.js
Original file line number Diff line number Diff line change
@@ -1,80 +1,64 @@
import React from 'react'
import React, { useState } from 'react'
import PropTypes from 'prop-types'
import { sortedBy } from '_/helpers'

import style from './sharedStyle.css'
import { Tooltip } from '_/components/tooltips'
// import style from './sharedStyle.css'
import { withMsg } from '_/intl'
import { Select, SelectOption, SelectVariant } from '@patternfly/react-core'

const NOBREAK_SPACE = '\u00A0'

const MarkAsDefault = withMsg(({ msg, value, isDefault }) => {
if (!isDefault) {
return value
}
return (<>{value}{NOBREAK_SPACE}<i>{msg.defaultOption()}</i></>
)
})

/*
* TODO: Update this to use a patternfly-react component. Probably use a width styled
* __DropdownButton__ with a set of scrolling __MenuItem__s.
*/

const SelectBox = ({ sort, items = [], locale, selected, onChange, validationState, id, disabled }) => {
const SelectBox = ({ msg, sort, items = [], locale, selected, onChange, validationState, id, disabled }) => {
const [open, setOpen] = useState(false)
if (sort) {
sortedBy(items, 'value', locale)
}
const options = items.map(({ id, value: name, isDefault }) => ({ id, name, isDefault, toString: () => name }))
const selectedId = selected ?? items?.[0]?.id ?? null
const validationClass = validationState === 'error' ? style['selectBox-has-error'] : ''
const selectedItem = items.find(item => item.id === selectedId)
const selectedOption = options.find(option => option.id === selectedId)

const onSelect = (event, { id = '' } = {}, isPlaceholder) => {
if (!isPlaceholder) {
setOpen(false)
onChange(id)
}
}
return (
<div style={{ width: '100%' }} id={id}>
<div className='dropdown'>
<Tooltip id={`${id}-selectbox-tooltip`} placement={'bottom'} tooltip={selectedItem ? selectedItem.value : ''}>
<button
className={`btn btn-default dropdown-toggle ${style['dropdown-button']} ${validationClass}`}
type='button'
data-toggle='dropdown'
id={`${id}-button-toggle`}
disabled={disabled}
>
<span className={style['dropdown-button-text']} id={`${id}-button-text`} >
{selectedItem ? <MarkAsDefault {...selectedItem} /> : NOBREAK_SPACE}
</span>
<span className='caret' id={`${id}-button-caret`} />
</button>
</Tooltip>
<ul className={`dropdown-menu ${style.dropdown}`} role='menu'>
{items.map(item => (
<li role='presentation' className={item.id === selectedId ? 'selected' : ''} key={item.id}>
<a role='menuitem' tabIndex='-1' onClick={() => onChange(item.id)} id={`${id}-item-${item.value}`}>
{<MarkAsDefault {...item} />}
</a>
</li>
))}
</ul>
</div>
</div>
<>
<Select
id={id}
variant={SelectVariant.single}
onToggle={setOpen}
onSelect={onSelect}
selections={selectedOption}
isOpen={open}
isDisabled={disabled}
validated={validationState}
>
{options.map((option) => (
<SelectOption
key={option.id}
value={option}
description={option.isDefault ? msg.defaultOption() : undefined}
/>
))}
</Select>
</>
)
}

SelectBox.propTypes = {
/* eslint-disable react/no-unused-prop-types */
selected: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), // id of a selected item, false-ish for the first item
items: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
value: PropTypes.string,
isDefault: PropTypes.bool,
})).isRequired, // Array<{ id: string, value: string }>, order matters if sort is false-ish
sort: PropTypes.bool, // sorted alphabetically by current locale with { numeric: true } if true
/* eslint-enable react/no-unused-prop-types */
onChange: PropTypes.func.isRequired, // (selectedId: string) => any
id: PropTypes.string,
validationState: PropTypes.oneOf([false, 'default', 'error']),
disabled: PropTypes.bool,
locale: PropTypes.string.isRequired,
msg: PropTypes.object.isRequired,
}

export default withMsg(SelectBox)
101 changes: 49 additions & 52 deletions src/components/Settings/SettingsBase.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,86 +2,83 @@ import React from 'react'
import PropTypes from 'prop-types'
import {
Card,
Col,
ControlLabel,
CardBody,
CardTitle,
FormGroup,
} from 'patternfly-react'
} from '@patternfly/react-core'
import { InfoTooltip } from '_/components/tooltips'

import style from './style.css'

const LabelCol = ({ children, tooltip, fieldPath, ...props }) => {
return (
<Col componentClass={ControlLabel} {...props}>
{ children } { tooltip && <InfoTooltip tooltip={tooltip} id={`${fieldPath}-info-tooltip`} /> }
</Col>
)
}
LabelCol.propTypes = {
children: PropTypes.node.isRequired,
tooltip: PropTypes.string,
fieldPath: PropTypes.string,
}

const Item = ({ title, isActive, onClick }) => {
return (
<li className={`list-group-item ${isActive && 'active'}`}>
<a href='#' onClick={(e) => { e.preventDefault(); onClick() }}>
<span className='list-group-item-value'>{title}</span>
<div className='badge-container-pf' />
</a>
</li>
)
}

Item.propTypes = {
title: PropTypes.string.isRequired,
isActive: PropTypes.bool,
onClick: PropTypes.func.isRequired,
}

const Section = ({ name, section }) => (
<>
<h3>
const Section = ({ name, section, className }) => (
<Card className={className}>
<CardTitle>
<a id={name} />
{section.title}
{ section.tooltip && <InfoTooltip id={`${name}-info-tooltip`} tooltip={section.tooltip} /> }
</h3>
</CardTitle>
{ section.fields.map((field) => (
<FormGroup key={field.name} className={style['settings-field']}>
<LabelCol fieldPath={`${name}-${field.name}`} tooltip={field.tooltip} sm={3} className={style['field-label']}>
{ field.title }
</LabelCol>
<Col sm={9}>
{field.body}
</Col>
</FormGroup>
<CardBody key={field.key}>
{
field.title && (
<FormGroup
label={field.title}
labelIcon={field.tooltip && <InfoTooltip tooltip={field.tooltip} id={`${name}-${field.key}-info-tooltip`} /> }
fieldId={field.fieldId}
>
{field.body}
</FormGroup>
)}

{!field.title && field.body}

{ !field.title && field.tooltip && <InfoTooltip tooltip={field.tooltip} id={`${name}-${field.key}-info-tooltip`} /> }
</CardBody>
)) }
</>
</Card>
)

Section.propTypes = {
name: PropTypes.string.isRequired,
section: PropTypes.object.isRequired,
className: PropTypes.string,
}

const SettingsBase = ({ name, section }) => {
const sections = section.sections ? Object.entries(section.sections) : [[name, section]]
return (
<div className={style['search-content-box']}>
{ sections.map(([name, section]) => (
<Card key={name} className={style['main-content']}>
<div className={style['main-content-container']}>
<Section name={name} section={section} />
</div>
</Card>
<Section key={name} name={name} section={section} className={style['main-content']}/>
)
)}
</div>
)
}
SettingsBase.propTypes = {
section: PropTypes.object.isRequired,
section: PropTypes.shape({
sections: PropTypes.objectOf(PropTypes.shape({
title: PropTypes.string,
tooltip: PropTypes.string,
fields: PropTypes.arrayOf(PropTypes.shape({
title: PropTypes.string,
tooltip: PropTypes.string,
key: PropTypes.string,
body: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
fieldId: PropTypes.string,
})),
})),
title: PropTypes.string,
tooltip: PropTypes.string,
fields: PropTypes.arrayOf(PropTypes.shape({
title: PropTypes.string,
tooltip: PropTypes.string,
key: PropTypes.string,
body: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
fieldId: PropTypes.string,
})),

}).isRequired,
name: PropTypes.string.isRequired,
}

Expand Down
Loading

0 comments on commit 2fec83e

Please sign in to comment.