diff --git a/src/components/common/DynamicListForm.jsx b/src/components/common/DynamicListForm.jsx new file mode 100644 index 000000000..70976f794 --- /dev/null +++ b/src/components/common/DynamicListForm.jsx @@ -0,0 +1,94 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { Button } from "@patternfly/react-core/dist/esm/components/Button"; +import { EmptyState, EmptyStateBody } from "@patternfly/react-core/dist/esm/components/EmptyState"; +import { FormFieldGroup, FormFieldGroupHeader } from "@patternfly/react-core/dist/esm/components/Form"; +import { HelperText, HelperTextItem } from "@patternfly/react-core/dist/esm/components/HelperText"; + +import './DynamicListForm.scss'; + +export class DynamicListForm extends React.Component { + constructor(props) { + super(props); + this.state = { + list: [], + }; + this.keyCounter = 0; + this.removeItem = this.removeItem.bind(this); + this.addItem = this.addItem.bind(this); + this.onItemChange = this.onItemChange.bind(this); + } + + removeItem(idx, field, value) { + this.setState(state => { + const items = state.list.concat(); + items.splice(idx, 1); + return { list: items }; + }, () => this.props.onChange(this.state.list.concat())); + } + + addItem() { + this.setState(state => { + return { list: [...state.list, Object.assign({ key: this.keyCounter++ }, this.props.default)] }; + }, () => this.props.onChange(this.state.list.concat())); + } + + onItemChange(idx, field, value) { + this.setState(state => { + const items = state.list.concat(); + items[idx][field] = value || null; + return { list: items }; + }, () => this.props.onChange(this.state.list.concat())); + } + + render () { + const { id, label, actionLabel, formclass, emptyStateString, helperText } = this.props; + const dialogValues = this.state; + return ( + {actionLabel}} + /> + } className={"dynamic-form-group " + formclass}> + { + dialogValues.list.length + ? <> + {dialogValues.list.map((item, idx) => { + return React.cloneElement(this.props.itemcomponent, { + idx, + item, + id: id + "-" + idx, + key: idx, + onChange: this.onItemChange, + removeitem: this.removeItem, + additem: this.addItem, + options: this.props.options, + itemCount: Object.keys(dialogValues.list).length, + }); + }) + } + {helperText && + + {helperText} + + } + + : + + {emptyStateString} + + + } + + ); + } +} +DynamicListForm.propTypes = { + emptyStateString: PropTypes.string.isRequired, + onChange: PropTypes.func.isRequired, + id: PropTypes.string.isRequired, + itemcomponent: PropTypes.object.isRequired, + formclass: PropTypes.string, + options: PropTypes.object, +}; diff --git a/src/components/common/DynamicListForm.scss b/src/components/common/DynamicListForm.scss new file mode 100644 index 000000000..1dfe40db8 --- /dev/null +++ b/src/components/common/DynamicListForm.scss @@ -0,0 +1,39 @@ +@import "global-variables"; + +.dynamic-form-group { + .pf-v5-c-empty-state { + padding: 0; + } + + .pf-v5-c-form__label { + // Don't allow labels to wrap + white-space: nowrap; + } + + .remove-button-group { + // Move 'Remove' button the the end of the row + grid-column: -1; + // Move 'Remove' button to the bottom of the line so as to align with the other form fields + display: flex; + align-items: flex-end; + } + + // Set check to the same height as input widgets and vertically align + .pf-v5-c-form__group-control > .pf-v5-c-check { + // Set height to the same as inputs + // Font height is font size * line height (1rem * 1.5) + // Widgets have 5px padding, 1px border (top & bottom): (5 + 1) * 2 = 12 + // This all equals to 36px + height: calc(var(--pf-v5-global--FontSize--md) * var(--pf-v5-global--LineHeight--md) + 12px); + align-content: center; + } + + // We use FormFieldGroup PF component for the nested look and for ability to add buttons to the header + // However we want to save space and not add indent to the left so we need to override it + .pf-v5-c-form__field-group-body { + // Stretch content fully + --pf-v5-c-form__field-group-body--GridColumn: 1 / -1; + // Reduce padding at the top + --pf-v5-c-form__field-group-body--PaddingTop: var(--pf-v5-global--spacer--xs); + } +}