Skip to content

Commit

Permalink
Make it a dynamic disclosure list
Browse files Browse the repository at this point in the history
  • Loading branch information
Kadrian committed Apr 15, 2024
1 parent d7d8cee commit 5e18a25
Show file tree
Hide file tree
Showing 6 changed files with 193 additions and 89 deletions.
2 changes: 1 addition & 1 deletion frontend/mock-api/forms/export-form.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
},
{
"name": "theDisclosure",
"type": "DISCLOSURE",
"type": "DISCLOSURE_LIST",
"label": {
"de": "Datenschutz"
},
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/js/external-forms/config-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ interface TranslatableString {

export type Forms = Form[];

export type FormField = Field | Tabs | Group | Disclosure;
export type NonFormField = Headline | Description;
export type FormField = Field | Tabs | Group | Disclosure;
export type FormFieldWithValue = Exclude<FormField, Group>;

export type GeneralField = FormField | NonFormField;

Expand All @@ -37,7 +38,7 @@ export interface Group {
}

export interface Disclosure {
type: "DISCLOSURE";
type: "DISCLOSURE_LIST";
creatable?: boolean;
defaultOpen?: boolean;
name: string;
Expand Down
26 changes: 13 additions & 13 deletions frontend/src/js/external-forms/form/Field.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ import { useDatasetId } from "../../dataset/selectors";
import type { Language } from "../../localization/useActiveLang";
import type { GeneralField } from "../config-types";
import { Description } from "../form-components/Description";
import { getInitialValue, isFormField } from "../helper";
import { getInitialValue, isFormFieldWithValue } from "../helper";

import type { DynamicFormValues } from "./Form";
import { CheckboxField } from "./fields/CheckboxField";
import { ConceptListField } from "./fields/ConceptListField";
import { DatasetSelectField } from "./fields/DatasetSelectField";
import { DateRangeField } from "./fields/DateRangeField";
import { DisclosureField } from "./fields/DisclosureField";
import { DisclosureListField } from "./fields/DisclosureListField";
import { GroupField } from "./fields/GroupField";
import { HeadlineField } from "./fields/HeadlineField";
import { NumberField } from "./fields/NumberField";
Expand All @@ -39,14 +39,13 @@ const Field = ({
const datasetId = useDatasetId();
const { locale, availableDatasets } = commonProps;

const defaultValue =
isFormField(field) && field.type !== "GROUP"
? getInitialValue(field, {
availableDatasets,
activeLang: locale,
datasetId,
})
: null;
const defaultValue = isFormFieldWithValue(field)
? getInitialValue(field, {
availableDatasets,
activeLang: locale,
datasetId,
})
: null;

switch (field.type) {
case "HEADLINE":
Expand Down Expand Up @@ -121,12 +120,13 @@ const Field = ({
datasetId={datasetId}
/>
);
case "DISCLOSURE":
case "DISCLOSURE_LIST":
return (
<DisclosureField
<DisclosureListField
field={field}
defaultValue={defaultValue}
commonProps={commonProps}
key={field.name}
datasetId={datasetId}
/>
);
case "GROUP":
Expand Down
71 changes: 0 additions & 71 deletions frontend/src/js/external-forms/form/fields/DisclosureField.tsx

This file was deleted.

156 changes: 156 additions & 0 deletions frontend/src/js/external-forms/form/fields/DisclosureListField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import {
faAdd,
faChevronDown,
faChevronRight,
faTimes,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { ComponentProps, useState } from "react";
import { useFieldArray } from "react-hook-form";
import tw from "tailwind-styled-components";
import IconButton from "../../../button/IconButton";
import { TransparentButton } from "../../../button/TransparentButton";
import { exists } from "../../../common/helpers/exists";
import FaIcon from "../../../icon/FaIcon";
import InfoTooltip from "../../../tooltip/InfoTooltip";
import { Disclosure } from "../../config-types";
import {
getFieldKey,
getInitialValue,
isFormFieldWithValue,
} from "../../helper";
import Field from "../Field";

const Summary = tw("summary")`
relative
cursor-pointer
flex
items-center
justify-between
gap-3
py-3
pl-3
pr-10
bg-white
text-sm
font-normal
`;

const DisclosureField = ({
field,
index,
remove,
canRemove,
commonProps,
}: {
field: Disclosure;
index: number;
remove: (index: number) => void;
canRemove?: boolean;
commonProps: Omit<ComponentProps<typeof Field>, "field">;
}) => {
const [isOpen, setOpen] = useState(false);

if (field.fields.length === 0) return null;

const { formType, locale } = commonProps;

return (
<details
className="overflow-hidden rounded border border-gray-400"
open={isOpen}
onToggle={() => setOpen(!isOpen)}
>
<Summary>
<div className="flex items-center gap-3">
<span className="w-5">
<FaIcon icon={isOpen ? faChevronDown : faChevronRight} />
</span>
{field.label[locale]}
{exists(field.tooltip) && (
<InfoTooltip text={field.tooltip[locale]} />
)}
</div>
{field.creatable && canRemove && (
<IconButton
className="absolute right-0 top-1/2 -translate-y-1/2"
icon={faTimes}
onClick={() => remove(index)}
/>
)}
</Summary>
<div className="flex flex-col gap-2 bg-bg-50 border-t border-gray-300 p-3">
{field.fields.map((f, i) => {
const key = getFieldKey(formType, f, i);
const childField = isFormFieldWithValue(f)
? { ...f, name: `${field.name}[${index}].${f.name}` }
: f;

console.log(childField.name);

return <Field key={key} field={childField} {...commonProps} />;
})}
</div>
</details>
);
};

export const DisclosureListField = ({
field,
defaultValue,
commonProps,
datasetId,
}: {
field: Disclosure;
defaultValue: unknown;
commonProps: Omit<ComponentProps<typeof Field>, "field">;
datasetId: string | null;
}) => {
const { fields, append, remove } = useFieldArray({
control: commonProps.control,
name: field.name,
});
console.log(field, defaultValue);
console.log(fields);

if (field.fields.length === 0) return null;

const { locale } = commonProps;

return (
<div className="space-y-2">
{fields.map((fd, index) => (
<DisclosureField
key={fd.id}
field={field}
index={index}
remove={remove}
canRemove={fields.length > 1}
commonProps={commonProps}
/>
))}
{field.creatable && (
<TransparentButton
className="w-full"
small
onClick={() =>
append(
Object.fromEntries(
field.fields.filter(isFormFieldWithValue).map((f) => [
f.name,
getInitialValue(f, {
activeLang: locale,
availableDatasets: commonProps.availableDatasets,
datasetId,
}),
]),
),
)
}
>
<FontAwesomeIcon icon={faAdd} />
</TransparentButton>
)}
</div>
);
};
22 changes: 20 additions & 2 deletions frontend/src/js/external-forms/helper.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import type { DatasetT, SelectOptionT } from "../api/types";
import type { Language } from "../localization/useActiveLang";

import type { FormField, GeneralField, Group } from "./config-types";
import type {
FormField,
FormFieldWithValue,
GeneralField,
} from "./config-types";

const nonFormFieldTypes = new Set(["HEADLINE", "DESCRIPTION"]);

Expand Down Expand Up @@ -46,6 +50,12 @@ export const isFormField = (field: GeneralField): field is FormField => {
return !nonFormFieldTypes.has(field.type);
};

export const isFormFieldWithValue = (
field: GeneralField,
): field is FormFieldWithValue => {
return isFormField(field) && field.type !== "GROUP";
};

export function collectAllFormFields(fields: GeneralField[]): FormField[] {
return fields.filter(isFormField).flatMap((field) => {
if (field.type === "GROUP") {
Expand All @@ -62,7 +72,7 @@ export function collectAllFormFields(fields: GeneralField[]): FormField[] {
}

export function getInitialValue(
field: Exclude<FormField, Group>,
field: FormFieldWithValue,
context: {
availableDatasets: SelectOptionT[];
activeLang: Language;
Expand Down Expand Up @@ -106,6 +116,14 @@ export function getInitialValue(
min: null,
max: null,
};
case "DISCLOSURE_LIST":
return [
Object.fromEntries(
field.fields
.filter(isFormFieldWithValue)
.map((f) => [f.name, getInitialValue(f, context)]),
),
];
default:
return field.defaultValue || undefined;
}
Expand Down

0 comments on commit 5e18a25

Please sign in to comment.