From 5ed54d872a70c2ba3c0a727d99093335e03f7a77 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sun, 31 Dec 2023 15:25:03 -0300 Subject: updat web utils --- .../src/handlers/FormProvider.tsx | 4 +- packages/web-util/src/forms/Calendar.tsx | 119 +++++++++++++++++++++ packages/web-util/src/forms/Dialog.tsx | 15 +++ packages/web-util/src/forms/FormProvider.tsx | 93 +++++++++++----- .../src/forms/InputAbsoluteTime.stories.tsx | 60 +++++++++++ packages/web-util/src/forms/InputAbsoluteTime.tsx | 77 +++++++++++++ .../web-util/src/forms/InputAmount.stories.tsx | 59 ++++++++++ packages/web-util/src/forms/InputAmount.tsx | 6 +- packages/web-util/src/forms/InputArray.stories.tsx | 79 ++++++++++++++ packages/web-util/src/forms/InputArray.tsx | 43 ++++---- .../src/forms/InputChoiceHorizontal.stories.tsx | 69 ++++++++++++ .../web-util/src/forms/InputChoiceHorizontal.tsx | 15 ++- .../src/forms/InputChoiceStacked.stories.tsx | 69 ++++++++++++ packages/web-util/src/forms/InputChoiceStacked.tsx | 4 +- packages/web-util/src/forms/InputFile.stories.tsx | 64 +++++++++++ packages/web-util/src/forms/InputFile.tsx | 95 ++++++++-------- .../web-util/src/forms/InputInteger.stories.tsx | 55 ++++++++++ packages/web-util/src/forms/InputInteger.tsx | 3 +- packages/web-util/src/forms/InputLine.stories.tsx | 59 ++++++++++ packages/web-util/src/forms/InputLine.tsx | 80 ++++++-------- .../src/forms/InputSelectMultiple.stories.tsx | 90 ++++++++++++++++ .../web-util/src/forms/InputSelectMultiple.tsx | 19 ++-- .../web-util/src/forms/InputSelectOne.stories.tsx | 70 ++++++++++++ packages/web-util/src/forms/InputSelectOne.tsx | 11 +- packages/web-util/src/forms/InputText.stories.tsx | 59 ++++++++++ packages/web-util/src/forms/InputText.tsx | 3 +- .../web-util/src/forms/InputTextArea.stories.tsx | 59 ++++++++++ packages/web-util/src/forms/InputTextArea.tsx | 3 +- .../web-util/src/forms/InputToggle.stories.tsx | 59 ++++++++++ packages/web-util/src/forms/InputToggle.tsx | 38 +++++++ packages/web-util/src/forms/NiceForm.tsx | 60 +++++++++++ packages/web-util/src/forms/TimePicker.tsx | 110 +++++++++++++++++++ packages/web-util/src/forms/forms.ts | 22 ++-- packages/web-util/src/forms/index.stories.ts | 13 +++ packages/web-util/src/forms/useField.ts | 12 ++- 35 files changed, 1519 insertions(+), 177 deletions(-) create mode 100644 packages/web-util/src/forms/Calendar.tsx create mode 100644 packages/web-util/src/forms/Dialog.tsx create mode 100644 packages/web-util/src/forms/InputAbsoluteTime.stories.tsx create mode 100644 packages/web-util/src/forms/InputAbsoluteTime.tsx create mode 100644 packages/web-util/src/forms/InputAmount.stories.tsx create mode 100644 packages/web-util/src/forms/InputArray.stories.tsx create mode 100644 packages/web-util/src/forms/InputChoiceHorizontal.stories.tsx create mode 100644 packages/web-util/src/forms/InputChoiceStacked.stories.tsx create mode 100644 packages/web-util/src/forms/InputFile.stories.tsx create mode 100644 packages/web-util/src/forms/InputInteger.stories.tsx create mode 100644 packages/web-util/src/forms/InputLine.stories.tsx create mode 100644 packages/web-util/src/forms/InputSelectMultiple.stories.tsx create mode 100644 packages/web-util/src/forms/InputSelectOne.stories.tsx create mode 100644 packages/web-util/src/forms/InputText.stories.tsx create mode 100644 packages/web-util/src/forms/InputTextArea.stories.tsx create mode 100644 packages/web-util/src/forms/InputToggle.stories.tsx create mode 100644 packages/web-util/src/forms/InputToggle.tsx create mode 100644 packages/web-util/src/forms/NiceForm.tsx create mode 100644 packages/web-util/src/forms/TimePicker.tsx create mode 100644 packages/web-util/src/forms/index.stories.ts diff --git a/packages/aml-backoffice-ui/src/handlers/FormProvider.tsx b/packages/aml-backoffice-ui/src/handlers/FormProvider.tsx index b3cb7a972..b9f9f7832 100644 --- a/packages/aml-backoffice-ui/src/handlers/FormProvider.tsx +++ b/packages/aml-backoffice-ui/src/handlers/FormProvider.tsx @@ -32,14 +32,14 @@ export const FormContext = createContext>({}); * - object => recurse into * - array => behavior result and element field */ -export type FormState = { +export type FormState = { [field in keyof T]?: T[field] extends AbsoluteTime ? BehaviorResult : T[field] extends AmountJson ? BehaviorResult : T[field] extends Array ? InputArrayFieldState

- : T[field] extends (object) + : T[field] extends (object | undefined) ? FormState : BehaviorResult; }; diff --git a/packages/web-util/src/forms/Calendar.tsx b/packages/web-util/src/forms/Calendar.tsx new file mode 100644 index 000000000..e476bf6f6 --- /dev/null +++ b/packages/web-util/src/forms/Calendar.tsx @@ -0,0 +1,119 @@ +import { AbsoluteTime } from "@gnu-taler/taler-util" +import { useTranslationContext } from "@gnu-taler/web-util/browser" +import { add as dateAdd, sub as dateSub, eachDayOfInterval, endOfMonth, endOfWeek, format, getMonth, getYear, isSameDay, isSameMonth, startOfDay, startOfMonth, startOfWeek } from "date-fns" +import { VNode, h } from "preact" +import { useState } from "preact/hooks" + +export function Calendar({ value, onChange }: { value: AbsoluteTime | undefined, onChange: (v: AbsoluteTime) => void }): VNode { + const today = startOfDay(new Date()) + const selected = !value ? today : new Date(AbsoluteTime.toStampMs(value)) + const [showingDate, setShowingDate] = useState(selected) + const month = getMonth(showingDate) + const year = getYear(showingDate) + + const start = startOfWeek(startOfMonth(showingDate)); + const end = endOfWeek(endOfMonth(showingDate)); + const daysInMonth = eachDayOfInterval({ start, end }); + const { i18n } = useTranslationContext() + const monthNames = [ + i18n.str`January`, + i18n.str`February`, + i18n.str`March`, + i18n.str`April`, + i18n.str`May`, + i18n.str`June`, + i18n.str`July`, + i18n.str`August`, + i18n.str`September`, + i18n.str`October`, + i18n.str`November`, + i18n.str`December`, + ] + return

+
+ +
{year}
+ +
+
+ +
{monthNames[month]}
+ +
+
+
M
+
T
+
W
+
T
+
F
+
S
+
S
+
+
+
+ {daysInMonth.map(current => ( + + ))} +
+ {daysInMonth.length < 40 ?
: undefined} +
+
+} diff --git a/packages/web-util/src/forms/Dialog.tsx b/packages/web-util/src/forms/Dialog.tsx new file mode 100644 index 000000000..7b41fe487 --- /dev/null +++ b/packages/web-util/src/forms/Dialog.tsx @@ -0,0 +1,15 @@ +import { ComponentChildren, VNode, h } from "preact"; + +export function Dialog({ children, onClose }: { onClose?: () => void; children: ComponentChildren }): VNode { + return +} diff --git a/packages/web-util/src/forms/FormProvider.tsx b/packages/web-util/src/forms/FormProvider.tsx index 3da2a4f07..b9f9f7832 100644 --- a/packages/web-util/src/forms/FormProvider.tsx +++ b/packages/web-util/src/forms/FormProvider.tsx @@ -7,14 +7,13 @@ import { ComponentChildren, VNode, createContext, h } from "preact"; import { MutableRef, StateUpdater, - useEffect, - useRef, - useState, + useState } from "preact/hooks"; -export interface FormType { +export interface FormType { value: MutableRef>; initialValue?: Partial; + readOnly?: boolean; onUpdate?: StateUpdater; computeFormState?: (v: T) => FormState; } @@ -22,18 +21,31 @@ export interface FormType { //@ts-ignore export const FormContext = createContext>({}); -export type FormState = { +/** + * Map of {[field]:BehaviorResult} + * for every field of type + * - any native (string, number, etc...) + * - absoluteTime + * - amountJson + * + * except for: + * - object => recurse into + * - array => behavior result and element field + */ +export type FormState = { [field in keyof T]?: T[field] extends AbsoluteTime - ? Partial - : T[field] extends AmountJson - ? Partial - : T[field] extends Array - ? Partial> - : T[field] extends (object | undefined) - ? FormState - : Partial; + ? BehaviorResult + : T[field] extends AmountJson + ? BehaviorResult + : T[field] extends Array + ? InputArrayFieldState

+ : T[field] extends (object | undefined) + ? FormState + : BehaviorResult; }; +export type BehaviorResult = Partial & FieldUIOptions + export interface InputFieldState { /* should show the error */ error?: TranslatedString; @@ -45,41 +57,70 @@ export interface InputFieldState { hidden: boolean; } -export interface InputArrayFieldState extends InputFieldState { - elements: FormState[]; +export interface IconAddon { + type: "icon"; + icon: VNode; +} +export interface ButtonAddon { + type: "button"; + onClick: () => void; + children: ComponentChildren; +} +export interface TextAddon { + type: "text"; + text: TranslatedString; } +export type Addon = IconAddon | ButtonAddon | TextAddon; -export function FormProvider({ +export interface StringConverter { + toStringUI: (v?: T) => string; + fromStringUI: (v?: string) => T; +} + +type FieldUIOptions = { + placeholder?: TranslatedString; + tooltip?: TranslatedString; + help?: TranslatedString; + required?: boolean; +} + +export interface UIFormProps extends FieldUIOptions { + name: K; + label: TranslatedString; + before?: Addon; + after?: Addon; + converter?: StringConverter; +} + +export interface InputArrayFieldState

extends BehaviorResult { + elements?: FormState

[]; +} + +export function FormProvider({ children, initialValue, onUpdate: notify, onSubmit, computeFormState, + readOnly, }: { initialValue?: Partial; onUpdate?: (v: Partial) => void; onSubmit?: (v: Partial, s: FormState | undefined) => void; computeFormState?: (v: Partial) => FormState; + readOnly?: boolean; children: ComponentChildren; }): VNode { - // const value = useRef(initialValue ?? {}); - // useEffect(() => { - // return function onUnload() { - // value.current = initialValue ?? {}; - // }; - // }); - // const onUpdate = notify + const [state, setState] = useState>(initialValue ?? {}); const value = { current: state }; - // console.log("RENDER", initialValue, value); const onUpdate = (v: typeof state) => { - // console.log("updated"); setState(v); if (notify) notify(v); }; return (

{ diff --git a/packages/web-util/src/forms/InputAbsoluteTime.stories.tsx b/packages/web-util/src/forms/InputAbsoluteTime.stories.tsx new file mode 100644 index 000000000..54e41ffae --- /dev/null +++ b/packages/web-util/src/forms/InputAbsoluteTime.stories.tsx @@ -0,0 +1,60 @@ +/* + This file is part of GNU Taler + (C) 2022 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see + */ + +/** + * + * @author Sebastian Javier Marchano (sebasjm) + */ + +import { AbsoluteTime, TranslatedString } from "@gnu-taler/taler-util"; +import * as tests from "@gnu-taler/web-util/testing"; +import { + NiceForm as TestedComponent, +} from "./NiceForm.js"; +import { FlexibleForm } from "./forms.js"; + +export default { + title: "Input Absolute Time", +}; + +export namespace Simplest { + export interface Form { + comment: string; + } +} + +type TargetObject = { + today: AbsoluteTime; +} +const initial: TargetObject = { + today: AbsoluteTime.now() +} + +const form: FlexibleForm = { + design: [{ + title: "this is a simple form" as TranslatedString, + fields: [{ + type: "absoluteTime", + props: { + label: "label of the field" as TranslatedString, + name: "today", + pattern: "dd/MM/yyyy HH:mm" + }, + }] + }] +} + +export const SimpleComment = tests.createExample(TestedComponent, { initial, form }); diff --git a/packages/web-util/src/forms/InputAbsoluteTime.tsx b/packages/web-util/src/forms/InputAbsoluteTime.tsx new file mode 100644 index 000000000..0e03c5595 --- /dev/null +++ b/packages/web-util/src/forms/InputAbsoluteTime.tsx @@ -0,0 +1,77 @@ +import { AbsoluteTime } from "@gnu-taler/taler-util"; +import { InputLine } from "./InputLine.js"; +import { Fragment, VNode, h } from "preact"; +import { format, parse } from "date-fns"; +import { Dialog } from "./Dialog.js"; +import { Calendar } from "./Calendar.js"; +import { useState } from "preact/hooks"; +import { useField } from "./useField.js"; +import { UIFormProps } from "./FormProvider.js"; +import { TimePicker } from "./TimePicker.js"; + +export function InputAbsoluteTime( + props: { pattern?: string } & UIFormProps, +): VNode { + const pattern = props.pattern ?? "dd/MM/yyyy"; + const [open, setOpen] = useState(true) + const { value, onChange } = useField(props.name); + return ( + + + + type="text" + after={{ + type: "button", + onClick: () => { + setOpen(true) + }, + // icon: , + children: ( + + + ) + }} + converter={{ + //@ts-ignore + fromStringUI: (v): AbsoluteTime | undefined => { + if (!v) return undefined; + try { + const t_ms = parse(v, pattern, Date.now()).getTime(); + return AbsoluteTime.fromMilliseconds(t_ms); + } catch (e) { + return undefined; + } + }, + //@ts-ignore + toStringUI: (v: AbsoluteTime | undefined) => { + return !v || !v.t_ms + ? undefined + : v.t_ms === "never" + ? "never" + : format(v.t_ms, pattern); + }, + }} + {...props} + /> + {/* {open && + setOpen(false)}> + { + onChange(v as any) + setOpen(false) + }} /> + + } */} + {open && + setOpen(false)} > + { + onChange(v as any) + }} + onConfirm={() => { + setOpen(false) + }} /> + } + + ); +} diff --git a/packages/web-util/src/forms/InputAmount.stories.tsx b/packages/web-util/src/forms/InputAmount.stories.tsx new file mode 100644 index 000000000..872726247 --- /dev/null +++ b/packages/web-util/src/forms/InputAmount.stories.tsx @@ -0,0 +1,59 @@ +/* + This file is part of GNU Taler + (C) 2022 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see + */ + +/** + * + * @author Sebastian Javier Marchano (sebasjm) + */ + +import { AmountJson, Amounts, TranslatedString } from "@gnu-taler/taler-util"; +import * as tests from "@gnu-taler/web-util/testing"; +import { + NiceForm as TestedComponent, +} from "./NiceForm.js"; +import { FlexibleForm } from "./forms.js"; + +export default { + title: "Input Amount", +}; + +export namespace Simplest { + export interface Form { + comment: string; + } +} + +type TargetObject = { + amount: AmountJson; +} +const initial: TargetObject = { + amount: Amounts.parseOrThrow("USD:10") +} + +const form: FlexibleForm = { + design: [{ + title: "this is a simple form" as TranslatedString, + fields: [{ + type: "amount", + props: { + label: "label of the field" as TranslatedString, + name: "amount", + }, + }] + }] +} + +export const SimpleComment = tests.createExample(TestedComponent, { initial, form }); diff --git a/packages/web-util/src/forms/InputAmount.tsx b/packages/web-util/src/forms/InputAmount.tsx index 9be9dd4d0..29ec43525 100644 --- a/packages/web-util/src/forms/InputAmount.tsx +++ b/packages/web-util/src/forms/InputAmount.tsx @@ -1,7 +1,8 @@ import { AmountJson, Amounts, TranslatedString } from "@gnu-taler/taler-util"; import { VNode, h } from "preact"; -import { InputLine, UIFormProps } from "./InputLine.js"; +import { InputLine } from "./InputLine.js"; import { useField } from "./useField.js"; +import { UIFormProps } from "./FormProvider.js"; export function InputAmount( props: { currency?: string } & UIFormProps, @@ -21,7 +22,8 @@ export function InputAmount( converter={{ //@ts-ignore fromStringUI: (v): AmountJson => { - return Amounts.parseOrThrow(`${currency}:${v}`); + + return Amounts.parse(`${currency}:${v}`) ?? Amounts.zeroOfCurrency(currency); }, //@ts-ignore toStringUI: (v: AmountJson) => { diff --git a/packages/web-util/src/forms/InputArray.stories.tsx b/packages/web-util/src/forms/InputArray.stories.tsx new file mode 100644 index 000000000..ee25d355b --- /dev/null +++ b/packages/web-util/src/forms/InputArray.stories.tsx @@ -0,0 +1,79 @@ +/* + This file is part of GNU Taler + (C) 2022 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see + */ + +/** + * + * @author Sebastian Javier Marchano (sebasjm) + */ + +import { TranslatedString } from "@gnu-taler/taler-util"; +import * as tests from "@gnu-taler/web-util/testing"; +import { + NiceForm as TestedComponent, +} from "./NiceForm.js"; +import { FlexibleForm } from "./forms.js"; + +export default { + title: "Input Array", +}; + +export namespace Simplest { + export interface Form { + comment: string; + } +} + +type TargetObject = { + people: { + name: string; + age: number; + }[]; +} +const initial: TargetObject = { + people: [{ + name: "me", + age: 17, + }] +} + +const form: FlexibleForm = { + design: [{ + title: "this is a simple form" as TranslatedString, + fields: [{ + type: "array", + props: { + label: "People" as TranslatedString, + name: "comment", + fields: [{ + type: "text", + props: { + label: "the name" as TranslatedString, + name: "name", + } + }, { + type: "integer", + props: { + label: "the age" as TranslatedString, + name: "age", + } + }], + labelField: "name" + }, + }] + }] +} + +export const SimpleComment = tests.createExample(TestedComponent, { initial, form }); diff --git a/packages/web-util/src/forms/InputArray.tsx b/packages/web-util/src/forms/InputArray.tsx index 00379bed6..38c399e66 100644 --- a/packages/web-util/src/forms/InputArray.tsx +++ b/packages/web-util/src/forms/InputArray.tsx @@ -1,10 +1,10 @@ +import { TranslatedString } from "@gnu-taler/taler-util"; import { Fragment, VNode, h } from "preact"; -import { useEffect, useState } from "preact/hooks"; -import { FormProvider, InputArrayFieldState } from "./FormProvider.js"; -import { LabelWithTooltipMaybeRequired, UIFormProps } from "./InputLine.js"; +import { useState } from "preact/hooks"; +import { FormProvider, UIFormProps } from "./FormProvider.js"; +import { LabelWithTooltipMaybeRequired } from "./InputLine.js"; import { RenderAllFieldsByUiConfig, UIFormField } from "./forms.js"; import { useField } from "./useField.js"; -import { TranslatedString } from "@gnu-taler/taler-util"; function Option({ label, @@ -107,22 +107,24 @@ export function InputArray( /> ); })} -
-
+ {!state.disabled && +
+
+ }
{selectedIndex !== undefined && ( /** @@ -131,6 +133,7 @@ export function InputArray( */ { // current state is ignored // the state is defined by the parent form diff --git a/packages/web-util/src/forms/InputChoiceHorizontal.stories.tsx b/packages/web-util/src/forms/InputChoiceHorizontal.stories.tsx new file mode 100644 index 000000000..7872afac7 --- /dev/null +++ b/packages/web-util/src/forms/InputChoiceHorizontal.stories.tsx @@ -0,0 +1,69 @@ +/* + This file is part of GNU Taler + (C) 2022 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see + */ + +/** + * + * @author Sebastian Javier Marchano (sebasjm) + */ + +import { TranslatedString } from "@gnu-taler/taler-util"; +import * as tests from "@gnu-taler/web-util/testing"; +import { + NiceForm as TestedComponent, +} from "./NiceForm.js"; +import { FlexibleForm } from "./forms.js"; + +export default { + title: "Input Choice Horizontal", +}; + +export namespace Simplest { + export interface Form { + comment: string; + } +} + +type TargetObject = { + comment: string; +} +const initial: TargetObject = { + comment: "0" +} + +const form: FlexibleForm = { + design: [{ + title: "this is a simple form" as TranslatedString, + fields: [{ + type: "choiceHorizontal", + props: { + label: "label of the field" as TranslatedString, + name: "comment", + choices: [{ + label: "first choice" as TranslatedString, + value: "1" + }, { + label: "second choice" as TranslatedString, + value: "2" + }, { + label: "thrid choice" as TranslatedString, + value: "3" + },], + }, + }] + }] +} + +export const SimpleComment = tests.createExample(TestedComponent, { initial, form }); diff --git a/packages/web-util/src/forms/InputChoiceHorizontal.tsx b/packages/web-util/src/forms/InputChoiceHorizontal.tsx index 5c909b5d7..594b1c32e 100644 --- a/packages/web-util/src/forms/InputChoiceHorizontal.tsx +++ b/packages/web-util/src/forms/InputChoiceHorizontal.tsx @@ -1,8 +1,13 @@ import { TranslatedString } from "@gnu-taler/taler-util"; import { Fragment, VNode, h } from "preact"; -import { LabelWithTooltipMaybeRequired, UIFormProps } from "./InputLine.js"; +import { LabelWithTooltipMaybeRequired } from "./InputLine.js"; import { useField } from "./useField.js"; -import { Choice } from "./InputChoiceStacked.js"; +import { UIFormProps } from "./FormProvider.js"; + +export interface Choice { + label: TranslatedString; + value: V; +} export function InputChoiceHorizontal( props: { @@ -57,6 +62,8 @@ export function InputChoiceHorizontal( return ( ); })} diff --git a/packages/web-util/src/forms/InputChoiceStacked.stories.tsx b/packages/web-util/src/forms/InputChoiceStacked.stories.tsx new file mode 100644 index 000000000..215418430 --- /dev/null +++ b/packages/web-util/src/forms/InputChoiceStacked.stories.tsx @@ -0,0 +1,69 @@ +/* + This file is part of GNU Taler + (C) 2022 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see + */ + +/** + * + * @author Sebastian Javier Marchano (sebasjm) + */ + +import { TranslatedString } from "@gnu-taler/taler-util"; +import * as tests from "@gnu-taler/web-util/testing"; +import { + NiceForm as TestedComponent, +} from "./NiceForm.js"; +import { FlexibleForm } from "./forms.js"; + +export default { + title: "Input Choice Stacked", +}; + +export namespace Simplest { + export interface Form { + comment: string; + } +} + +type TargetObject = { + comment: string; +} +const initial: TargetObject = { + comment: "some initial comment" +} + +const form: FlexibleForm = { + design: [{ + title: "this is a simple form" as TranslatedString, + fields: [{ + type: "choiceStacked", + props: { + label: "label of the field" as TranslatedString, + name: "comment", + choices: [{ + label: "first choice" as TranslatedString, + value: "1" + }, { + label: "second choice" as TranslatedString, + value: "2" + }, { + label: "thrid choice" as TranslatedString, + value: "3" + },], + }, + }] + }] +} + +export const SimpleComment = tests.createExample(TestedComponent, { initial, form }); diff --git a/packages/web-util/src/forms/InputChoiceStacked.tsx b/packages/web-util/src/forms/InputChoiceStacked.tsx index c37984368..48d367ff2 100644 --- a/packages/web-util/src/forms/InputChoiceStacked.tsx +++ b/packages/web-util/src/forms/InputChoiceStacked.tsx @@ -1,7 +1,8 @@ import { TranslatedString } from "@gnu-taler/taler-util"; import { Fragment, VNode, h } from "preact"; -import { LabelWithTooltipMaybeRequired, UIFormProps } from "./InputLine.js"; +import { LabelWithTooltipMaybeRequired } from "./InputLine.js"; import { useField } from "./useField.js"; +import { UIFormProps } from "./FormProvider.js"; export interface Choice { label: TranslatedString; @@ -60,6 +61,7 @@ export function InputChoiceStacked( type="radio" name="server-size" // defaultValue={choice.value} + disabled={state.disabled} value={ (!converter ? (choice.value as string) diff --git a/packages/web-util/src/forms/InputFile.stories.tsx b/packages/web-util/src/forms/InputFile.stories.tsx new file mode 100644 index 000000000..8a1783bda --- /dev/null +++ b/packages/web-util/src/forms/InputFile.stories.tsx @@ -0,0 +1,64 @@ +/* + This file is part of GNU Taler + (C) 2022 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see + */ + +/** + * + * @author Sebastian Javier Marchano (sebasjm) + */ + +import { TranslatedString } from "@gnu-taler/taler-util"; +import * as tests from "@gnu-taler/web-util/testing"; +import { + NiceForm as TestedComponent, +} from "./NiceForm.js"; +import { FlexibleForm } from "./forms.js"; + +export default { + title: "Input File", +}; + +export namespace Simplest { + export interface Form { + comment: string; + } +} + +type TargetObject = { + comment: string; +} +const initial: TargetObject = { + comment: "some initial comment" +} + +const form: FlexibleForm = { + design: [{ + title: "this is a simple form" as TranslatedString, + fields: [{ + type: "file", + props: { + label: "label of the field" as TranslatedString, + name: "comment", + required: true, + maxBites: 2 * 1024 * 1024, + accept: ".png", + tooltip: "this is a very long tooltip that explain what the field does without being short" as TranslatedString, + help: "Max size of 2 mega bytes" as TranslatedString, + }, + }] + }] +} + +export const SimpleComment = tests.createExample(TestedComponent, { initial, form }); diff --git a/packages/web-util/src/forms/InputFile.tsx b/packages/web-util/src/forms/InputFile.tsx index 0d89a98a3..bc460f370 100644 --- a/packages/web-util/src/forms/InputFile.tsx +++ b/packages/web-util/src/forms/InputFile.tsx @@ -1,6 +1,7 @@ import { Fragment, VNode, h } from "preact"; -import { LabelWithTooltipMaybeRequired, UIFormProps } from "./InputLine.js"; +import { LabelWithTooltipMaybeRequired } from "./InputLine.js"; import { useField } from "./useField.js"; +import { UIFormProps, BehaviorResult } from "./FormProvider.js"; export function InputFile( props: { maxBites: number; accept?: string } & UIFormProps, @@ -11,12 +12,12 @@ export function InputFile( placeholder, tooltip, required, - help, + help: propsHelp, maxBites, accept, } = props; const { value, onChange, state } = useField(name); - + const help = propsHelp ?? state.help if (state.hidden) { return
; } @@ -42,40 +43,42 @@ export function InputFile( clip-rule="evenodd" /> -
- - {/*

or drag and drop

*/} -
+ {!state.disabled && +
+ + {/*

or drag and drop

*/} +
+ }
) : ( @@ -85,14 +88,16 @@ export function InputFile( class=" h-24 w-full object-cover relative" /> -
{ - onChange(undefined!); - }} - > - Clear -
+ {!state.disabled && +
{ + onChange(undefined!); + }} + > + Clear +
+ } )} {help &&

{help}

} diff --git a/packages/web-util/src/forms/InputInteger.stories.tsx b/packages/web-util/src/forms/InputInteger.stories.tsx new file mode 100644 index 000000000..344865817 --- /dev/null +++ b/packages/web-util/src/forms/InputInteger.stories.tsx @@ -0,0 +1,55 @@ +/* + This file is part of GNU Taler + (C) 2022 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see + */ + +/** + * + * @author Sebastian Javier Marchano (sebasjm) + */ + +import { TranslatedString } from "@gnu-taler/taler-util"; +import * as tests from "@gnu-taler/web-util/testing"; +import { + NiceForm as TestedComponent, +} from "./NiceForm.js"; +import { FlexibleForm } from "./forms.js"; + +export default { + title: "Input Integer", +}; + + +type TargetObject = { + age: number; +} +const initial: TargetObject = { + age: 5, +} + +const form: FlexibleForm = { + design: [{ + title: "this is a simple form" as TranslatedString, + fields: [{ + type: "integer", + props: { + label: "label of the field" as TranslatedString, + name: "age", + tooltip: "just numbers" as TranslatedString, + }, + }] + }] +} + +export const SimpleComment = tests.createExample(TestedComponent, { initial, form }); diff --git a/packages/web-util/src/forms/InputInteger.tsx b/packages/web-util/src/forms/InputInteger.tsx index fb04e3852..a6a02ad43 100644 --- a/packages/web-util/src/forms/InputInteger.tsx +++ b/packages/web-util/src/forms/InputInteger.tsx @@ -1,5 +1,6 @@ import { VNode, h } from "preact"; -import { InputLine, UIFormProps } from "./InputLine.js"; +import { InputLine } from "./InputLine.js"; +import { UIFormProps } from "./FormProvider.js"; export function InputInteger( props: UIFormProps, diff --git a/packages/web-util/src/forms/InputLine.stories.tsx b/packages/web-util/src/forms/InputLine.stories.tsx new file mode 100644 index 000000000..0d55bddf7 --- /dev/null +++ b/packages/web-util/src/forms/InputLine.stories.tsx @@ -0,0 +1,59 @@ +/* + This file is part of GNU Taler + (C) 2022 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see + */ + +/** + * + * @author Sebastian Javier Marchano (sebasjm) + */ + +import { TranslatedString } from "@gnu-taler/taler-util"; +import * as tests from "@gnu-taler/web-util/testing"; +import { + NiceForm as TestedComponent, +} from "./NiceForm.js"; +import { FlexibleForm } from "./forms.js"; + +export default { + title: "Input Line", +}; + +export namespace Simplest { + export interface Form { + comment: string; + } +} + +type TargetObject = { + comment: string; +} +const initial: TargetObject = { + comment: "some initial comment" +} + +const form: FlexibleForm = { + design: [{ + title: "this is a simple form" as TranslatedString, + fields: [{ + type: "text", + props: { + label: "label of the field" as TranslatedString, + name: "comment", + }, + }] + }] +} + +export const SimpleComment = tests.createExample(TestedComponent, { initial, form }); diff --git a/packages/web-util/src/forms/InputLine.tsx b/packages/web-util/src/forms/InputLine.tsx index 9448ef5e4..8c44b1ca5 100644 --- a/packages/web-util/src/forms/InputLine.tsx +++ b/packages/web-util/src/forms/InputLine.tsx @@ -1,42 +1,8 @@ import { TranslatedString } from "@gnu-taler/taler-util"; import { ComponentChildren, Fragment, VNode, h } from "preact"; import { useField } from "./useField.js"; - -export interface IconAddon { - type: "icon"; - icon: VNode; -} -interface ButtonAddon { - type: "button"; - onClick: () => void; - children: ComponentChildren; -} -interface TextAddon { - type: "text"; - text: TranslatedString; -} -type Addon = IconAddon | ButtonAddon | TextAddon; - -interface StringConverter { - toStringUI: (v?: T) => string; - fromStringUI: (v?: string) => T; -} - -export interface UIFormProps { - name: K; - label: TranslatedString; - placeholder?: TranslatedString; - tooltip?: TranslatedString; - help?: TranslatedString; - before?: Addon; - after?: Addon; - required?: boolean; - converter?: StringConverter; -} - -export type FormErrors = { - [P in keyof T]?: string | FormErrors; -}; +import { useEffect, useState } from "preact/hooks"; +import { UIFormProps } from "./FormProvider.js"; //@ts-ignore const TooltipIcon = ( @@ -80,11 +46,11 @@ export function LabelWithTooltipMaybeRequired({ {Label} {TooltipIcon} - @@ -110,8 +76,9 @@ function InputWrapper({ after, help, error, + disabled, required, -}: { error?: string; children: ComponentChildren } & UIFormProps): VNode { +}: { error?: string; disabled: boolean, children: ComponentChildren } & UIFormProps): VNode { return (
({ ) : before.type === "button" ? (