Skip to content

Commit

Permalink
Merge pull request #369 from IQSS/feature/sub-branch/dynamic-fields-a…
Browse files Browse the repository at this point in the history
…nd-validation

Dynamically render form field in Create Dataset Form + Fields Validations
  • Loading branch information
ekraffmiller authored Apr 17, 2024
2 parents 5107f18 + 321a41c commit 24b8ff0
Show file tree
Hide file tree
Showing 58 changed files with 6,663 additions and 683 deletions.
8 changes: 8 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,14 @@
"trailingComma": "none",
"bracketSameLine": true
}
],
"@typescript-eslint/no-misused-promises": [
"error",
{
"checksVoidReturn": {
"attributes": false
}
}
]
},
"overrides": [
Expand Down
2 changes: 1 addition & 1 deletion .stylelintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"selector-pseudo-class-no-unknown": [
true,
{
"ignorePseudoClasses": ["export"]
"ignorePseudoClasses": ["export", "global"]
}
],
"property-no-unknown": [
Expand Down
24 changes: 20 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
},
"dependencies": {
"@faker-js/faker": "7.6.0",
"@iqss/dataverse-client-javascript": "2.0.0-pr134.0d63f4b",
"@iqss/dataverse-client-javascript": "2.0.0-pr141.153a56a",
"@iqss/dataverse-design-system": "*",
"@istanbuljs/nyc-config-typescript": "1.0.2",
"@tanstack/react-table": "8.9.2",
Expand All @@ -33,6 +33,7 @@
"moment-timezone": "0.5.43",
"react-bootstrap": "2.7.2",
"react-bootstrap-icons": "1.10.3",
"react-hook-form": "7.51.2",
"react-i18next": "12.1.5",
"react-infinite-scroll-hook": "4.1.1",
"react-loader-spinner": "5.3.4",
Expand Down
15 changes: 15 additions & 0 deletions packages/design-system/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,21 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

# Non Published Changes

- **Accordion:** extend Props Interface to accept HTML Attributes props.
- **AccordionBody:** extend Props Interface to accept HTML Attributes, `bsPrefix` and `as` props.
- **AccordionHeader:** extend Props Interface to accept HTML Attributes, `onClick`, `bsPrefix` and `as` props.
- **AccordionItem:** extend Props Interface to accept HTML Attributes, `bsPrefix` and `as` props.
- **FormChecboxGroup:** refactor styles.
- **FormGroupWithMultipleFields:** refactor styles and conditional render logic.
- **FormGroup:** ability to clone children wrapped by react fragments.
- **FormCheckbox:** ability to forward react ref to input.
- **FormInput:** ability to forward react ref to input.
- **FormSelect:** ability to forward react ref to input.
- **FormTextArea:** ability to forward react ref to input.
- **FormFeedback:** remove `span: 9` from styles.

# [1.1.0](https://github.com/IQSS/dataverse-frontend/compare/@iqss/[email protected]...@iqss/[email protected]) (2024-03-12)

### Bug Fixes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ import { AccordionItem } from './AccordionItem'
import { AccordionBody } from './AccordionBody'
import { AccordionHeader } from './AccordionHeader'

interface AccordionProps {
interface AccordionProps extends Omit<React.HTMLAttributes<HTMLElement>, 'onSelect'> {
defaultActiveKey?: string[] | string
alwaysOpen?: boolean
children: ReactNode
}

function Accordion({ defaultActiveKey, children, alwaysOpen = false }: AccordionProps) {
function Accordion({ defaultActiveKey, alwaysOpen = false, children, ...rest }: AccordionProps) {
return (
<AccordionBS defaultActiveKey={defaultActiveKey} alwaysOpen={alwaysOpen}>
<AccordionBS defaultActiveKey={defaultActiveKey} alwaysOpen={alwaysOpen} {...rest}>
{children}
</AccordionBS>
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { ReactNode } from 'react'
import { ElementType, ReactNode } from 'react'
import { Accordion as AccordionBS } from 'react-bootstrap'

interface AccordionBodyProps {
interface AccordionBodyProps extends React.HTMLAttributes<HTMLElement> {
children: ReactNode
bsPrefix?: string
as?: ElementType
}

export function AccordionBody({ children }: AccordionBodyProps) {
return <AccordionBS.Body>{children}</AccordionBS.Body>
export function AccordionBody({ children, ...rest }: AccordionBodyProps) {
return <AccordionBS.Body {...rest}>{children}</AccordionBS.Body>
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { ReactNode } from 'react'
import { ElementType, ReactNode } from 'react'
import { Accordion as AccordionBS } from 'react-bootstrap'

interface AccordionHeaderProps {
interface AccordionHeaderProps extends React.HTMLAttributes<HTMLElement> {
children: ReactNode
onClick?: () => void
bsPrefix?: string
as?: ElementType
}

export function AccordionHeader({ children }: AccordionHeaderProps) {
return <AccordionBS.Header>{children}</AccordionBS.Header>
export function AccordionHeader({ children, ...rest }: AccordionHeaderProps) {
return <AccordionBS.Header {...rest}>{children}</AccordionBS.Header>
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { ReactNode } from 'react'
import { ElementType, ReactNode } from 'react'
import { Accordion as AccordionBS } from 'react-bootstrap'

interface AccordionItemProps {
export interface AccordionItemProps extends React.HTMLAttributes<HTMLElement> {
eventKey: string
children: ReactNode
bsPrefix?: string
as?: ElementType
}

export function AccordionItem({ eventKey, children }: AccordionItemProps) {
return <AccordionBS.Item eventKey={eventKey}>{children}</AccordionBS.Item>
export function AccordionItem({ children, ...rest }: AccordionItemProps) {
return <AccordionBS.Item {...rest}>{children}</AccordionBS.Item>
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
@import "src/lib/assets/styles/design-tokens/typography.module";
@import 'src/lib/assets/styles/design-tokens/typography.module';

.title {
padding-top: calc(0.375rem + 1px);
padding-bottom: calc(0.375rem + 1px);
display: inline-block;
margin-bottom: 0.5rem;
font-weight: $dv-font-weight-bold;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export function FormCheckboxGroup({
}: PropsWithChildren<FormCheckboxGroupProps>) {
const validationClass = isInvalid ? 'is-invalid' : isValid ? 'is-valid' : ''
return (
<Row>
<Row className="mb-3">
<Col sm={3}>
<span className={styles.title}>
{title} {required && <RequiredInputSymbol />}{' '}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,21 @@ export function FormGroupWithMultipleFields({
const isFirstField = index == 0

return (
<Row key={index}>
<Row key={index} className="mb-3">
<Col sm={3}>
{isFirstField && <Title title={title} required={required} message={message} />}
</Col>
<Col sm={6}>{field}</Col>
<Col sm={3}>
{withDynamicFields && (
<Col sm={withDynamicFields ? 6 : 9}>{field}</Col>

{withDynamicFields && (
<Col sm={3}>
<DynamicFieldsButtons
originalField={isFirstField}
onAddButtonClick={() => addField(field)}
onRemoveButtonClick={() => removeField(index)}
/>
)}
</Col>
</Col>
)}
</Row>
)
})}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,7 @@ function FormGroup({
children,
...props
}: PropsWithChildren<FormGroupProps>) {
const childrenWithRequiredProp = React.Children.map(children as JSX.Element, (child) => {
return React.cloneElement(child, {
required: required,
withinMultipleFieldsGroup: as === Col
})
})
const childrenWithRequiredProp = cloneThroughFragments(children, required, as)

return (
<FormBS.Group
Expand All @@ -42,6 +37,29 @@ function FormGroup({
</FormBS.Group>
)
}
function cloneThroughFragments(
children: React.ReactNode,
required?: boolean,
as?: typeof Col | typeof Row
): React.ReactNode {
return React.Children.map(children, (child) => {
if (React.isValidElement(child)) {
if (child.type === React.Fragment) {
const hasChildren = (props: unknown): props is { children: React.ReactNode } =>
typeof props === 'object' && Object.hasOwnProperty.call(props, 'children')

if (hasChildren(child.props)) {
return cloneThroughFragments(child.props.children, required, as)
}
}
return React.cloneElement(child as React.ReactElement, {
required: required,
withinMultipleFieldsGroup: as === Col
})
}
return child
})
}

FormGroup.Label = FormLabel
FormGroup.Input = FormInput
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,22 @@ interface FormCheckboxProps extends Omit<React.InputHTMLAttributes<HTMLInputElem
validFeedback?: string
}

export function FormCheckbox({
label,
id,
isValid,
isInvalid,
validFeedback,
invalidFeedback,
...props
}: FormCheckboxProps) {
export const FormCheckbox = React.forwardRef(function FormCheckbox(
{ label, id, isValid, isInvalid, validFeedback, invalidFeedback, ...props }: FormCheckboxProps,
ref
) {
return (
<FormBS.Check type="checkbox" id={id}>
<FormBS.Check.Input type="checkbox" isValid={isValid} isInvalid={isInvalid} {...props} />
<FormBS.Check.Input
type="checkbox"
isValid={isValid}
isInvalid={isInvalid}
ref={ref as React.ForwardedRef<HTMLInputElement>}
{...props}
/>
<FormBS.Check.Label>{label}</FormBS.Check.Label>
<FormBS.Control.Feedback type="invalid">{invalidFeedback}</FormBS.Control.Feedback>
<FormBS.Control.Feedback type="valid">{validFeedback}</FormBS.Control.Feedback>
</FormBS.Check>
)
}
})
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export function FormFeedback({
return withinMultipleFieldsGroup ? (
<FormControl.Feedback type={type}>{children}</FormControl.Feedback>
) : (
<FormControl.Feedback as={Col} sm={{ offset: 3, span: 9 }} type={type}>
<FormControl.Feedback as={Col} sm={{ offset: 3 }} type={type}>
{children}
</FormControl.Feedback>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,21 @@ interface FormInputProps extends React.HTMLAttributes<FormInputElement> {
required?: boolean
}

export function FormInput({
type = 'text',
name,
readOnly,
isValid,
isInvalid,
disabled,
withinMultipleFieldsGroup,
value,
required,
...props
}: FormInputProps) {
export const FormInput = React.forwardRef(function FormInput(
{
type = 'text',
name,
readOnly,
isValid,
isInvalid,
disabled,
withinMultipleFieldsGroup,
value,
required,
...props
}: FormInputProps,
ref
) {
return (
<FormElementLayout
withinMultipleFieldsGroup={withinMultipleFieldsGroup}
Expand All @@ -43,8 +46,9 @@ export function FormInput({
disabled={disabled}
value={value}
required={required}
ref={ref as React.ForwardedRef<HTMLInputElement>}
{...props}
/>
</FormElementLayout>
)
}
})
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ interface FormSelectProps extends Omit<React.SelectHTMLAttributes<HTMLSelectElem
withinMultipleFieldsGroup?: boolean
}

export function FormSelect({
withinMultipleFieldsGroup,
children,
...props
}: PropsWithChildren<FormSelectProps>) {
export const FormSelect = React.forwardRef(function FormSelect(
{ withinMultipleFieldsGroup, children, ...props }: PropsWithChildren<FormSelectProps>,
ref
) {
return (
<FormElementLayout withinMultipleFieldsGroup={withinMultipleFieldsGroup}>
<FormBS.Select {...props}>{children}</FormBS.Select>
<FormBS.Select ref={ref as React.ForwardedRef<HTMLSelectElement>} {...props}>
{children}
</FormBS.Select>
</FormElementLayout>
)
}
})
Loading

0 comments on commit 24b8ff0

Please sign in to comment.