diff --git a/i18n/compiled/en.json b/i18n/compiled/en.json index fd11946..097fca9 100644 --- a/i18n/compiled/en.json +++ b/i18n/compiled/en.json @@ -1,4 +1,10 @@ { + "0uBorL": [ + { + "type": 0, + "value": "The text that will be displayed in the form step to go to the previous step" + } + ], "2vFy0E": [ { "type": 0, @@ -11,6 +17,12 @@ "value": "Preview" } ], + "4BhLtR": [ + { + "type": 0, + "value": "Form button labels" + } + ], "6UFbVC": [ { "type": 0, @@ -67,6 +79,12 @@ "value": "Form submission statistics" } ], + "FkU1o8": [ + { + "type": 0, + "value": "The text that will be displayed at the start of the form to indicate the user can begin to fill in the form" + } + ], "JX8BYx": [ { "type": 0, @@ -91,6 +109,12 @@ "value": "Your session has expired." } ], + "Ny2VNs": [ + { + "type": 0, + "value": "The text that will be displayed in the overview page to change a certain step" + } + ], "Qroj81": [ { "type": 0, @@ -103,6 +127,30 @@ "value": "Save" } ], + "Ufxo9U": [ + { + "type": 0, + "value": "Show step progression next to the form" + } + ], + "UujtUf": [ + { + "type": 0, + "value": "The text that will be displayed in the form step to save the current information" + } + ], + "WczS7x": [ + { + "type": 0, + "value": "The text that will be displayed in the form step to go to the next step" + } + ], + "ZeeBAX": [ + { + "type": 0, + "value": "The buttons in the form use global settings. The buttons below are a preview of the buttons that will be used in the form. To change the button labels, click the 'Edit buttons' button below." + } + ], "ZiDANM": [ { "type": 0, @@ -121,6 +169,30 @@ "value": "Active form" } ], + "eRPpMl": [ + { + "type": 0, + "value": "Theme" + } + ], + "exbKi7": [ + { + "type": 0, + "value": "Edit buttons" + } + ], + "k1QXND": [ + { + "type": 0, + "value": "Presentation" + } + ], + "k2Tngs": [ + { + "type": 0, + "value": "The text that will be displayed in the overview page to confirm the form is filled in correctly" + } + ], "nxMx4O": [ { "type": 0, @@ -151,6 +223,24 @@ "value": "Inactive form" } ], + "tJ1m3q": [ + { + "type": 0, + "value": "The text that will be displayed in the overview page to go to the previous step" + } + ], + "v9B7G6": [ + { + "type": 0, + "value": "Show current step number and total amount of steps below the form title" + } + ], + "z2aEGS": [ + { + "type": 0, + "value": "Save" + } + ], "zEFFxg": [ { "type": 0, diff --git a/i18n/compiled/nl.json b/i18n/compiled/nl.json index 8d019b6..cdd873c 100644 --- a/i18n/compiled/nl.json +++ b/i18n/compiled/nl.json @@ -1,4 +1,10 @@ { + "0uBorL": [ + { + "type": 0, + "value": "De tekst op de knop die aangeeft om naar de vorige stap terug te gaan" + } + ], "2vFy0E": [ { "type": 0, @@ -11,6 +17,12 @@ "value": "Preview" } ], + "4BhLtR": [ + { + "type": 0, + "value": "Knopteksten formulier" + } + ], "6UFbVC": [ { "type": 0, @@ -67,6 +79,12 @@ "value": "Inzendingstatistieken" } ], + "FkU1o8": [ + { + "type": 0, + "value": "De tekst op de knop die aangeeft dat het formulier gestart kan worden" + } + ], "JX8BYx": [ { "type": 0, @@ -91,6 +109,12 @@ "value": "Je sessie is verlopen." } ], + "Ny2VNs": [ + { + "type": 0, + "value": "De tekst op de overzichtspagina om gegevens van een stap te wijzigen" + } + ], "Qroj81": [ { "type": 0, @@ -103,6 +127,30 @@ "value": "Opslaan" } ], + "Ufxo9U": [ + { + "type": 0, + "value": "Toon voortgang aan zijkant" + } + ], + "UujtUf": [ + { + "type": 0, + "value": "De tekst op de knop die aangeeft om de huidige stap op te slaan" + } + ], + "WczS7x": [ + { + "type": 0, + "value": "De tekst op de knop die aangeeft om naar de volgende stap te gaan" + } + ], + "ZeeBAX": [ + { + "type": 0, + "value": "De knoppen in het formulier hebben een algemene instelling, het voorbeeld van de knoppen zie je hieronder. Wil je de tekst op de knoppen voor dit formulier aanpassen, klik dan op de knop ‘Knoppen aanpassen’." + } + ], "ZiDANM": [ { "type": 0, @@ -121,6 +169,30 @@ "value": "Formulier actief" } ], + "eRPpMl": [ + { + "type": 0, + "value": "Stijl formulier" + } + ], + "exbKi7": [ + { + "type": 0, + "value": "Knoppen aanpassen" + } + ], + "k1QXND": [ + { + "type": 0, + "value": "Weergave" + } + ], + "k2Tngs": [ + { + "type": 0, + "value": "De tekst om ingevulde gegevens te bevestigen op de overzichtspagina" + } + ], "nxMx4O": [ { "type": 0, @@ -151,6 +223,24 @@ "value": "Formulier niet actief" } ], + "tJ1m3q": [ + { + "type": 0, + "value": "De tekst op de overzichtspagina om naar de vorige stap terug te gaan" + } + ], + "v9B7G6": [ + { + "type": 0, + "value": "Toon aantal stappen onder titel formulier" + } + ], + "z2aEGS": [ + { + "type": 0, + "value": "Bevestigen" + } + ], "zEFFxg": [ { "type": 0, diff --git a/i18n/messages/en.json b/i18n/messages/en.json index 446d622..a93ee0f 100644 --- a/i18n/messages/en.json +++ b/i18n/messages/en.json @@ -1,4 +1,9 @@ { + "0uBorL": { + "defaultMessage": "The text that will be displayed in the form step to go to the previous step", + "description": "LiteralsForm field '_stepLiterals.previous_text' label", + "originalDefault": "The text that will be displayed in the form step to go to the previous step" + }, "2vFy0E": { "defaultMessage": "Categories", "description": "Route breadcrumb label for form categories", @@ -9,6 +14,11 @@ "description": "Preview button text", "originalDefault": "Preview" }, + "4BhLtR": { + "defaultMessage": "Form button labels", + "description": "Edit form button labels modal title", + "originalDefault": "Form button labels" + }, "6UFbVC": { "defaultMessage": "There was an authentication problem.", "description": "'Not authenticated' error message", @@ -29,6 +39,11 @@ "description": "Route breadcrumb label for form submission statistics", "originalDefault": "form submission statistics" }, + "FkU1o8": { + "defaultMessage": "The text that will be displayed at the start of the form to indicate the user can begin to fill in the form", + "description": "LiteralsForm field 'literals.begin_text' label", + "originalDefault": "The text that will be displayed at the start of the form to indicate the user can begin to fill in the form" + }, "JX8BYx": { "defaultMessage": "Unfortunately something went wrong!", "description": "'Generic' error message", @@ -49,6 +64,11 @@ "description": "Session expiry notice - expired", "originalDefault": "Your session has expired." }, + "Ny2VNs": { + "defaultMessage": "The text that will be displayed in the overview page to change a certain step", + "description": "LiteralsForm field 'literals.change_text' label", + "originalDefault": "The text that will be displayed in the overview page to change a certain step" + }, "Qroj81": { "defaultMessage": "Forms", "description": "Route breadcrumb label for forms overview", @@ -59,6 +79,26 @@ "description": "Save button text", "originalDefault": "Save" }, + "Ufxo9U": { + "defaultMessage": "Show step progression next to the form", + "description": "Form detail field 'showProgressIndicator' label", + "originalDefault": "Show step progression next to the form" + }, + "UujtUf": { + "defaultMessage": "The text that will be displayed in the form step to save the current information", + "description": "LiteralsForm field '_stepLiterals.save_text' label", + "originalDefault": "The text that will be displayed in the form step to save the current information" + }, + "WczS7x": { + "defaultMessage": "The text that will be displayed in the form step to go to the next step", + "description": "LiteralsForm field '_stepLiterals.next_text' label", + "originalDefault": "The text that will be displayed in the form step to go to the next step" + }, + "ZeeBAX": { + "defaultMessage": "The buttons in the form use global settings. The buttons below are a preview of the buttons that will be used in the form. To change the button labels, click the 'Edit buttons' button below.", + "description": "Form detail presentation settings form buttons preview description", + "originalDefault": "The buttons in the form use global settings. The buttons below are a preview of the buttons that will be used in the form. To change the button labels, click the 'Edit buttons' button below." + }, "ZiDANM": { "defaultMessage": "Home", "description": "Route breadcrumb label for home", @@ -74,6 +114,26 @@ "description": "Label for form status \"active\"", "originalDefault": "Active form" }, + "eRPpMl": { + "defaultMessage": "Theme", + "description": "Form detail field 'theme' label", + "originalDefault": "Theme" + }, + "exbKi7": { + "defaultMessage": "Edit buttons", + "description": "Edit form button labels button text", + "originalDefault": "Edit buttons" + }, + "k1QXND": { + "defaultMessage": "Presentation", + "description": "form detail presentation settings page title", + "originalDefault": "Presentation" + }, + "k2Tngs": { + "defaultMessage": "The text that will be displayed in the overview page to confirm the form is filled in correctly", + "description": "LiteralsForm field 'literals.confirm_text' label", + "originalDefault": "The text that will be displayed in the overview page to confirm the form is filled in correctly" + }, "nxMx4O": { "defaultMessage": "Authentication problem", "description": "'Not authenticated' error title", @@ -99,6 +159,21 @@ "description": "Label for form status \"inactive\"", "originalDefault": "Inactive form" }, + "tJ1m3q": { + "defaultMessage": "The text that will be displayed in the overview page to go to the previous step", + "description": "LiteralsForm field 'literals.previous_text' label", + "originalDefault": "The text that will be displayed in the overview page to go to the previous step" + }, + "v9B7G6": { + "defaultMessage": "Show current step number and total amount of steps below the form title", + "description": "Form detail field 'showSummaryProgress' label", + "originalDefault": "Show current step number and total amount of steps below the form title" + }, + "z2aEGS": { + "defaultMessage": "Save", + "description": "LiteralsForm 'Save' button label", + "originalDefault": "Save" + }, "zEFFxg": { "defaultMessage": "Oops!", "description": "'Generic' error title", diff --git a/i18n/messages/nl.json b/i18n/messages/nl.json index 4c357a5..b26455e 100644 --- a/i18n/messages/nl.json +++ b/i18n/messages/nl.json @@ -1,4 +1,9 @@ { + "0uBorL": { + "defaultMessage": "De tekst op de knop die aangeeft om naar de vorige stap terug te gaan", + "description": "LiteralsForm field '_stepLiterals.previous_text' label", + "originalDefault": "The text that will be displayed in the form step to go to the previous step" + }, "2vFy0E": { "defaultMessage": "Categorieën", "description": "Route breadcrumb label for form categories", @@ -10,6 +15,11 @@ "isTranslated": true, "originalDefault": "Preview" }, + "4BhLtR": { + "defaultMessage": "Knopteksten formulier", + "description": "Edit form button labels modal title", + "originalDefault": "Form button labels" + }, "6UFbVC": { "defaultMessage": "Je moet ingelogd zijn voor deze actie.", "description": "'Not authenticated' error message", @@ -30,6 +40,11 @@ "description": "Route breadcrumb label for form submission statistics", "originalDefault": "form submission statistics" }, + "FkU1o8": { + "defaultMessage": "De tekst op de knop die aangeeft dat het formulier gestart kan worden", + "description": "LiteralsForm field 'literals.begin_text' label", + "originalDefault": "The text that will be displayed at the start of the form to indicate the user can begin to fill in the form" + }, "JX8BYx": { "defaultMessage": "Er ging helaas iets fout!", "description": "'Generic' error message", @@ -50,6 +65,11 @@ "description": "Session expiry notice - expired", "originalDefault": "Your session has expired." }, + "Ny2VNs": { + "defaultMessage": "De tekst op de overzichtspagina om gegevens van een stap te wijzigen", + "description": "LiteralsForm field 'literals.change_text' label", + "originalDefault": "The text that will be displayed in the overview page to change a certain step" + }, "Qroj81": { "defaultMessage": "Formulieren", "description": "Route breadcrumb label for forms overview", @@ -60,6 +80,26 @@ "description": "Save button text", "originalDefault": "Save" }, + "Ufxo9U": { + "defaultMessage": "Toon voortgang aan zijkant", + "description": "Form detail field 'showProgressIndicator' label", + "originalDefault": "Show step progression next to the form" + }, + "UujtUf": { + "defaultMessage": "De tekst op de knop die aangeeft om de huidige stap op te slaan", + "description": "LiteralsForm field '_stepLiterals.save_text' label", + "originalDefault": "The text that will be displayed in the form step to save the current information" + }, + "WczS7x": { + "defaultMessage": "De tekst op de knop die aangeeft om naar de volgende stap te gaan", + "description": "LiteralsForm field '_stepLiterals.next_text' label", + "originalDefault": "The text that will be displayed in the form step to go to the next step" + }, + "ZeeBAX": { + "defaultMessage": "De knoppen in het formulier hebben een algemene instelling, het voorbeeld van de knoppen zie je hieronder. Wil je de tekst op de knoppen voor dit formulier aanpassen, klik dan op de knop ‘Knoppen aanpassen’.", + "description": "Form detail presentation settings form buttons preview description", + "originalDefault": "The buttons in the form use global settings. The buttons below are a preview of the buttons that will be used in the form. To change the button labels, click the 'Edit buttons' button below." + }, "ZiDANM": { "defaultMessage": "Home", "description": "Route breadcrumb label for home", @@ -75,6 +115,26 @@ "description": "Label for form status \"active\"", "originalDefault": "Active form" }, + "eRPpMl": { + "defaultMessage": "Stijl formulier", + "description": "Form detail field 'theme' label", + "originalDefault": "Theme" + }, + "exbKi7": { + "defaultMessage": "Knoppen aanpassen", + "description": "Edit form button labels button text", + "originalDefault": "Edit buttons" + }, + "k1QXND": { + "defaultMessage": "Weergave", + "description": "form detail presentation settings page title", + "originalDefault": "Presentation" + }, + "k2Tngs": { + "defaultMessage": "De tekst om ingevulde gegevens te bevestigen op de overzichtspagina", + "description": "LiteralsForm field 'literals.confirm_text' label", + "originalDefault": "The text that will be displayed in the overview page to confirm the form is filled in correctly" + }, "nxMx4O": { "defaultMessage": "Inlogprobleem", "description": "'Not authenticated' error title", @@ -100,6 +160,21 @@ "description": "Label for form status \"inactive\"", "originalDefault": "Inactive form" }, + "tJ1m3q": { + "defaultMessage": "De tekst op de overzichtspagina om naar de vorige stap terug te gaan", + "description": "LiteralsForm field 'literals.previous_text' label", + "originalDefault": "The text that will be displayed in the overview page to go to the previous step" + }, + "v9B7G6": { + "defaultMessage": "Toon aantal stappen onder titel formulier", + "description": "Form detail field 'showSummaryProgress' label", + "originalDefault": "Show current step number and total amount of steps below the form title" + }, + "z2aEGS": { + "defaultMessage": "Bevestigen", + "description": "LiteralsForm 'Save' button label", + "originalDefault": "Save" + }, "zEFFxg": { "defaultMessage": "Oeps!", "description": "'Generic' error title", diff --git a/src/api-mocks/form.ts b/src/api-mocks/form.ts index d91ef85..aff14e9 100644 --- a/src/api-mocks/form.ts +++ b/src/api-mocks/form.ts @@ -22,13 +22,11 @@ export const FORM_DEFAULTS: Form = { confirmationCosignEmailTitle: '', confirmationCosignEmailContent: '', - buttonLiterals: { - begin: 'Begin', - save: 'Save', - next: 'Next', - previous: 'Previous', - change: 'Change', - confirm: 'Confirm', + literals: { + previous_text: 'Previous page', + begin_text: 'Begin form', + change_text: 'Change', + confirm_text: 'Confirm', }, suspensionAllowed: true, @@ -36,7 +34,28 @@ export const FORM_DEFAULTS: Form = { showSummaryProgress: true, authenticationBackends: [], autoLoginAuthenticationBackend: undefined, - steps: [], + steps: [ + { + id: 0, + uuid: '493eb3ba-c674-4afb-9a37-569e373eec11', + + name: 'First step', + internalName: 'First step', + slug: 'first-step', + order: 0, + + literals: { + next_text: 'Next', + previous_text: 'Previous step', + save_text: 'Save current information', + }, + + configuration: {}, + isApplicable: true, + isReusable: false, + loginRequired: false, + }, + ], active: true, // Date string in ISO 8601 format diff --git a/src/api-mocks/index.ts b/src/api-mocks/index.ts index e93243a..8198588 100644 --- a/src/api-mocks/index.ts +++ b/src/api-mocks/index.ts @@ -2,6 +2,7 @@ import {BASE_URL} from './base'; import mswWorker from './msw-worker'; export * from './accounts'; +export * from './theme'; export * from './form'; export {BASE_URL, mswWorker}; diff --git a/src/api-mocks/theme.ts b/src/api-mocks/theme.ts new file mode 100644 index 0000000..a49e082 --- /dev/null +++ b/src/api-mocks/theme.ts @@ -0,0 +1,37 @@ +import type {Mock} from '@vitest/spy'; +import {HttpResponse, http} from 'msw'; + +import {SESSION_EXPIRES_IN_HEADER} from '@/guard/session/session-expiry'; +import type {Theme} from '@/types/theme'; + +import {BASE_URL} from './base'; + +const DEFAULT_THEMES: Theme[] = [ + { + name: 'Light theme', + uuid: 'f01dc38e-3668-48fa-8963-b6b6e2a9ed6d', + url: 'http://localhost:8080/api/v2/themes/f01dc38e-3668-48fa-8963-b6b6e2a9ed6d', + }, + { + name: 'Dark theme', + uuid: '3f2176b0-adf3-4b6e-a233-93578fac4166', + url: 'http://localhost:8080/api/v2/themes/3f2176b0-adf3-4b6e-a233-93578fac4166', + }, +]; + +export const mockThemesGet = (spy?: Mock) => + http.get( + `${BASE_URL}v2/themes`, + info => { + // Call the spy with the request info + if (spy) spy(info); + + return HttpResponse.json(DEFAULT_THEMES, { + headers: { + [SESSION_EXPIRES_IN_HEADER]: `3600`, // Set the session expiry to 1 hour + }, + status: 200, + }); + }, + {once: false} + ); diff --git a/src/components/form/FormField.scss b/src/components/form/FormField.scss index 383c080..8bb2c55 100644 --- a/src/components/form/FormField.scss +++ b/src/components/form/FormField.scss @@ -3,4 +3,10 @@ flex-direction: column; align-items: baseline; gap: var(--sizes-spacing-xs-v, 8px); + margin-block-end: var(--sizes-spacing-s-v, 16px); + + .mykn-select, + .mykn-input { + width: 100%; + } } diff --git a/src/components/form/index.ts b/src/components/form/index.ts new file mode 100644 index 0000000..30d0865 --- /dev/null +++ b/src/components/form/index.ts @@ -0,0 +1,5 @@ +export * from './Checkbox'; +export * from './RadioField'; +export * from './Select'; +export * from './TextField'; +export * from './Toggle'; diff --git a/src/components/layout/FormLayout.tsx b/src/components/layout/FormLayout.tsx index 996570b..73ed804 100644 --- a/src/components/layout/FormLayout.tsx +++ b/src/components/layout/FormLayout.tsx @@ -4,7 +4,7 @@ import {FormattedMessage} from 'react-intl'; import {Outlet, useLoaderData} from 'react-router'; import {queryClient, useFormMutation} from '@/queryClient'; -import type {Form} from '@/types/form'; +import type {InternalForm} from '@/types/form'; import BasicLayout from './BasicLayout'; @@ -16,10 +16,10 @@ import BasicLayout from './BasicLayout'; * separated FormLayoutInner component, to simplify the setup and testing. */ const FormLayout = () => { - const form = useLoaderData
(); + const form = useLoaderData(); const {mutate} = useFormMutation(queryClient, form.uuid); - const handleSubmit = (formDetails: Form) => { + const handleSubmit = (formDetails: InternalForm) => { mutate(formDetails); }; @@ -33,8 +33,8 @@ const FormLayout = () => { }; export interface FormLayoutInnerProps { - initialValues: Form; - onSubmit: (formData: Form) => void; + initialValues: InternalForm; + onSubmit: (formData: InternalForm) => void; } /** @@ -48,7 +48,7 @@ export const FormLayoutInner: React.FC ( - + initialValues={initialValues} validateOnChange={false} validateOnBlur={false} diff --git a/src/components/preview/FormButtonsPreview.scss b/src/components/preview/FormButtonsPreview.scss new file mode 100644 index 0000000..42db21f --- /dev/null +++ b/src/components/preview/FormButtonsPreview.scss @@ -0,0 +1,5 @@ +.openforms-form-buttons-preview { + &__toolbar { + flex-wrap: wrap; + } +} diff --git a/src/components/preview/FormButtonsPreview.tsx b/src/components/preview/FormButtonsPreview.tsx new file mode 100644 index 0000000..e16ee9a --- /dev/null +++ b/src/components/preview/FormButtonsPreview.tsx @@ -0,0 +1,43 @@ +import {Button, Card, Toolbar} from '@maykin-ui/admin-ui'; +import {clsx} from 'clsx'; +import {useFormikContext} from 'formik'; + +import type {InternalForm} from '@/types/form'; + +import './FormButtonsPreview.scss'; + +export interface FormButtonsPreviewProps { + className?: string; +} + +const FormButtonsPreview: React.FC = ({className}) => { + const {values} = useFormikContext(); + const {literals, _stepLiterals} = values; + + // @TODO this should be real preview of the form buttons + // i.e. using the formio-renderer buttons and styling from the selected theme + + return ( + + + {Object.entries(literals).map(([key, literal]) => ( + + ))} + {_stepLiterals && + Object.entries(_stepLiterals).map(([key, literal]) => ( + + ))} + + + ); +}; + +export default FormButtonsPreview; diff --git a/src/pages/form-detail/index.ts b/src/pages/form-detail/index.ts new file mode 100644 index 0000000..fa8ac58 --- /dev/null +++ b/src/pages/form-detail/index.ts @@ -0,0 +1 @@ +export * from './settings'; diff --git a/src/pages/form-detail/settings/Presentation/LiteralsForm.tsx b/src/pages/form-detail/settings/Presentation/LiteralsForm.tsx new file mode 100644 index 0000000..16ebff2 --- /dev/null +++ b/src/pages/form-detail/settings/Presentation/LiteralsForm.tsx @@ -0,0 +1,96 @@ +import {Button} from '@maykin-ui/admin-ui'; +import {Form, Formik} from 'formik'; +import {FormattedMessage} from 'react-intl'; + +import {TextField} from '@/components/form'; +import type {InternalForm} from '@/types/form'; + +export interface LiteralsFormData { + literals: InternalForm['literals']; + _stepLiterals: InternalForm['_stepLiterals']; +} + +interface LiteralsFormProps { + initialValues: LiteralsFormData; + onSubmit: (formData: LiteralsFormData) => Promise | void; +} + +const LiteralsForm: React.FC = ({initialValues, onSubmit}) => { + return ( + + initialValues={initialValues} + onSubmit={async values => await onSubmit(values)} + > + + + } + /> + + } + /> + + } + /> + + } + /> + + } + /> + + } + /> + + } + /> + + + + ); +}; + +export default LiteralsForm; diff --git a/src/pages/form-detail/settings/Presentation/Presentation.scss b/src/pages/form-detail/settings/Presentation/Presentation.scss new file mode 100644 index 0000000..6f57799 --- /dev/null +++ b/src/pages/form-detail/settings/Presentation/Presentation.scss @@ -0,0 +1,6 @@ +.openforms-page-presentation { + &__form-buttons-preview { + margin-block-start: var(--sizes-spacing-s-v, 16px); + margin-block-end: var(--sizes-spacing-s-v, 16px); + } +} diff --git a/src/pages/form-detail/settings/Presentation/Presentation.stories.tsx b/src/pages/form-detail/settings/Presentation/Presentation.stories.tsx new file mode 100644 index 0000000..42fb91c --- /dev/null +++ b/src/pages/form-detail/settings/Presentation/Presentation.stories.tsx @@ -0,0 +1,155 @@ +import type {Meta, StoryObj} from '@storybook/react-vite'; +import {expect, userEvent, waitFor, within} from 'storybook/test'; + +import {buildForm, mockFormDetailsGet, mockThemesGet} from '@/api-mocks'; +import {withFormLayout} from '@/sb-decorators'; + +import PresentationPage from './Presentation'; + +export default { + title: 'Pages / Form detail / Settings / Presentation', + component: PresentationPage, + decorators: [withFormLayout], + parameters: { + layout: 'fullscreen', + }, +} satisfies Meta; + +type Story = StoryObj; + +export const Default: Story = { + parameters: { + msw: { + handlers: [ + mockFormDetailsGet(buildForm({theme: 'f01dc38e-3668-48fa-8963-b6b6e2a9ed6d'})), + mockThemesGet(), + ], + }, + }, +}; + +export const Interaction: Story = { + parameters: { + msw: { + handlers: [ + mockFormDetailsGet( + buildForm({theme: '', showProgressIndicator: false, showSummaryProgress: false}) + ), + mockThemesGet(), + ], + }, + }, + play: async ({canvasElement, step}) => { + const canvas = within(canvasElement); + + const formButtonsPreview = within(await canvas.findByTestId('form-buttons-preview')); + const theme = await canvas.findByRole('combobox'); + const nativeSelectForTheme = theme.querySelector('select[hidden]')!; + + const showProgressIndicatorField = canvas.getByLabelText( + 'Show step progression next to the form' + ); + const showSummaryProgressField = canvas.getByLabelText( + 'Show current step number and total amount of steps below the form title' + ); + + await step('Initial values', () => { + // Initial values of inputs + expect(nativeSelectForTheme.value).toBe(''); + expect(showProgressIndicatorField).not.toBeChecked(); + expect(showSummaryProgressField).not.toBeChecked(); + + // Initial button literals + const previewButtons = formButtonsPreview.getAllByRole('button'); + expect(previewButtons).toHaveLength(7); + + expect(previewButtons[0]).toHaveTextContent('Previous page'); + expect(previewButtons[1]).toHaveTextContent('Begin form'); + expect(previewButtons[2]).toHaveTextContent('Change'); + expect(previewButtons[3]).toHaveTextContent('Confirm'); + expect(previewButtons[4]).toHaveTextContent('Next'); + expect(previewButtons[5]).toHaveTextContent('Previous step'); + expect(previewButtons[6]).toHaveTextContent('Save current information'); + }); + + await step('Entering values', async () => { + // Select theme + await userEvent.click(theme, {delay: 10}); + await userEvent.click(await within(theme).findByText('Light theme'), { + delay: 10, + }); + + await userEvent.click(showProgressIndicatorField); + await userEvent.click(showSummaryProgressField); + }); + + await step('Update button literals', async () => { + await userEvent.click(canvas.getByRole('button', {name: 'Edit buttons'})); + + // The modal with the literals form is supposed to be shown + const literalsForm = await canvas.findByTestId('literals-form'); + await waitFor(() => { + expect(literalsForm).toBeVisible(); + }); + + const literalInputs = within(literalsForm).getAllByRole('textbox'); + expect(literalInputs).toHaveLength(7); + + // Edit all literals + expect(literalInputs[0]).toHaveValue('Begin form'); + await userEvent.clear(literalInputs[0]); + await userEvent.type(literalInputs[0], 'Start this form'); + + expect(literalInputs[1]).toHaveValue('Previous page'); + await userEvent.clear(literalInputs[1]); + await userEvent.type(literalInputs[1], 'Go to the previous page'); + + expect(literalInputs[2]).toHaveValue('Change'); + await userEvent.clear(literalInputs[2]); + await userEvent.type(literalInputs[2], 'Edit step data'); + + expect(literalInputs[3]).toHaveValue('Confirm'); + await userEvent.clear(literalInputs[3]); + await userEvent.type(literalInputs[3], 'Submit form'); + + expect(literalInputs[4]).toHaveValue('Next'); + await userEvent.clear(literalInputs[4]); + await userEvent.type(literalInputs[4], 'Go to the next step'); + + expect(literalInputs[5]).toHaveValue('Previous step'); + await userEvent.clear(literalInputs[5]); + await userEvent.type(literalInputs[5], 'Go to the previous step'); + + expect(literalInputs[6]).toHaveValue('Save current information'); + await userEvent.clear(literalInputs[6]); + await userEvent.type(literalInputs[6], 'Save step data'); + + // Submit the literals form + await userEvent.click(within(literalsForm).getByRole('button', {name: 'Save'})); + + // The modal with the literals form is no-longer shown + await waitFor(() => { + expect(literalsForm).not.toBeVisible(); + }); + }); + + await step('Validating values', async () => { + // The category select has the value of the selected category uuid + expect(nativeSelectForTheme.value).toBe('f01dc38e-3668-48fa-8963-b6b6e2a9ed6d'); + expect(showProgressIndicatorField).toBeChecked(); + expect(showSummaryProgressField).toBeChecked(); + + // The button literals should have been updated + const previewButtons = formButtonsPreview.getAllByRole('button'); + expect(previewButtons).toHaveLength(7); + + expect(previewButtons[0]).toHaveTextContent('Go to the previous page'); + expect(previewButtons[1]).toHaveTextContent('Start this form'); + expect(previewButtons[2]).toHaveTextContent('Edit step data'); + expect(previewButtons[3]).toHaveTextContent('Submit form'); + expect(previewButtons[4]).toHaveTextContent('Go to the next step'); + expect(previewButtons[5]).toHaveTextContent('Go to the previous step'); + expect(previewButtons[6]).toHaveTextContent('Save step data'); + }); + }, +}; diff --git a/src/pages/form-detail/settings/Presentation/Presentation.tsx b/src/pages/form-detail/settings/Presentation/Presentation.tsx new file mode 100644 index 0000000..a20ca52 --- /dev/null +++ b/src/pages/form-detail/settings/Presentation/Presentation.tsx @@ -0,0 +1,126 @@ +import {Body, Button, Column, Grid, H2, H3, Modal, P} from '@maykin-ui/admin-ui'; +import {useQuery} from '@tanstack/react-query'; +import {useFormikContext} from 'formik'; +import {useState} from 'react'; +import {FormattedMessage} from 'react-intl'; + +import {BASE_URL} from '@/api-mocks'; +import {Checkbox, Select} from '@/components/form'; +import FormButtonsPreview from '@/components/preview/FormButtonsPreview'; +import type {InternalForm} from '@/types/form'; +import type {Theme} from '@/types/theme'; +import {apiCall} from '@/utils/fetch'; + +import type {LiteralsFormData} from './LiteralsForm'; +import LiteralsForm from './LiteralsForm'; +import './Presentation.scss'; + +const PresentationPage: React.FC = () => { + const {values, setFieldValue} = useFormikContext(); + const {literals, _stepLiterals} = values; + + const [isModalOpen, setModalOpen] = useState(false); + const {data = []} = useQuery({ + queryKey: ['form-themes'], + queryFn: () => apiCall(`${BASE_URL}v2/themes`).then(res => res.json()), + }); + + const themeOptions = data.map(theme => ({ + value: theme.uuid, + label: theme.name, + })); + + const openModal = () => setModalOpen(true); + const closeModal = () => setModalOpen(false); + + const updateLiterals = async (newLiterals: LiteralsFormData) => { + // Update formik values with new button labels + await setFieldValue('literals', newLiterals.literals); + await setFieldValue('_stepLiterals', newLiterals._stepLiterals); + + // Close modal after submission + closeModal(); + }; + + return ( +
+ + +

+ +

+