diff --git a/src/features/form/components/Label.tsx b/src/features/form/components/Label.tsx index a02fe35dbc..7fc6e368a5 100644 --- a/src/features/form/components/Label.tsx +++ b/src/features/form/components/Label.tsx @@ -12,10 +12,10 @@ export interface IFormLabelProps { labelText: any; id: string; language: ILanguage; - required: boolean; - readOnly: boolean; + required?: boolean; + readOnly?: boolean; labelSettings?: ILabelSettings; - helpText: string; + helpText: string | number | boolean | React.ReactNode | undefined | null; } export default function Label(props: IFormLabelProps) { diff --git a/src/features/form/components/Legend.tsx b/src/features/form/components/Legend.tsx index aac420dea6..c6130edc68 100644 --- a/src/features/form/components/Legend.tsx +++ b/src/features/form/components/Legend.tsx @@ -59,7 +59,7 @@ export default function Legend(props: IFormLegendProps) { {props.descriptionText && ( )} diff --git a/src/features/form/containers/Form.tsx b/src/features/form/containers/Form.tsx index 5031903421..3f148d8949 100644 --- a/src/features/form/containers/Form.tsx +++ b/src/features/form/containers/Form.tsx @@ -15,7 +15,7 @@ import { extractBottomButtons, hasRequiredFields, topLevelComponents } from 'src import { renderGenericComponent } from 'src/utils/layout'; import { getFormHasErrors, missingFieldsInLayoutValidations } from 'src/utils/validation'; import type { ILayoutGroup } from 'src/layout/Group/types'; -import type { ILayout, ILayoutComponent } from 'src/layout/layout'; +import type { ILayout, ILayoutComponent, RenderableGenericComponent } from 'src/layout/layout'; export function renderLayoutComponent( layoutComponent: ILayoutComponent | ILayoutGroup, @@ -44,7 +44,7 @@ export function renderLayoutComponent( } } -function GenericComponent(component: ILayoutComponent, layout: ILayout) { +function GenericComponent(component: RenderableGenericComponent, layout: ILayout) { return renderGenericComponent({ component, layout }); } diff --git a/src/features/form/containers/GroupContainer.tsx b/src/features/form/containers/GroupContainer.tsx index 3514b3462a..99f80adbc4 100644 --- a/src/features/form/containers/GroupContainer.tsx +++ b/src/features/form/containers/GroupContainer.tsx @@ -18,12 +18,13 @@ import { createRepeatingGroupComponents, getRepeatingGroupFilteredIndices } from import { getHiddenFieldsForGroup } from 'src/utils/layout'; import { renderValidationMessagesForComponent } from 'src/utils/render'; import type { ILayoutGroup } from 'src/layout/Group/types'; -import type { ILayoutComponent, ILayoutComponentOrGroup } from 'src/layout/layout'; +import type { ComponentInGroup, ILayoutComponent } from 'src/layout/layout'; import type { IRuntimeState } from 'src/types'; + export interface IGroupProps { id: string; container: ILayoutGroup; - components: ILayoutComponentOrGroup[]; + components: ComponentInGroup[]; triggers?: Triggers[]; } @@ -193,7 +194,7 @@ export function GroupContainer({ id, container, components }: IGroupProps): JSX. <> c[0] as ILayoutComponent)} + repeatingGroupDeepCopyComponents={repeatingGroupDeepCopyComponents.map((c) => c[0])} textResources={textResources} container={container} /> diff --git a/src/features/form/containers/RepeatingGroupTable.tsx b/src/features/form/containers/RepeatingGroupTable.tsx index de09adfbc6..50dfb9fccd 100644 --- a/src/features/form/containers/RepeatingGroupTable.tsx +++ b/src/features/form/containers/RepeatingGroupTable.tsx @@ -23,7 +23,7 @@ import { componentHasValidations, repeatingGroupHasValidations } from 'src/utils import type { IFormData } from 'src/features/form/data'; import type { ILayoutGroup } from 'src/layout/Group/types'; import type { ILayoutCompInput } from 'src/layout/Input/types'; -import type { ILayout, ILayoutComponent } from 'src/layout/layout'; +import type { ComponentInGroup, ILayout, ILayoutComponent } from 'src/layout/layout'; import type { IAttachments } from 'src/shared/resources/attachments'; import type { IOptions, IRepeatingGroups, ITextResource, ITextResourceBindings, IValidations } from 'src/types'; import type { ILanguage } from 'src/types/shared'; @@ -31,10 +31,10 @@ import type { ILanguage } from 'src/types/shared'; export interface IRepeatingGroupTableProps { id: string; container: ILayoutGroup; - components: (ILayoutComponent | ILayoutGroup)[]; + components: ComponentInGroup[]; repeatingGroupIndex: number; repeatingGroups: IRepeatingGroups | null; - repeatingGroupDeepCopyComponents: (ILayoutComponent | ILayoutGroup)[][]; + repeatingGroupDeepCopyComponents: ComponentInGroup[][]; hiddenFields: string[]; formData: IFormData; attachments: IAttachments; @@ -267,7 +267,9 @@ export function RepeatingGroupTable({ } const childGroupIndex = repeatingGroups[childGroup.id]?.index; - const childGroupComponents = layout.filter((childElement) => childGroup.children?.indexOf(childElement.id) > -1); + const childGroupComponents = layout.filter( + (childElement) => childGroup.children?.indexOf(childElement.id) > -1, + ) as ComponentInGroup[]; const childRenderComponents = setupGroupComponents( childGroupComponents, childGroup.dataModelBindings?.group, diff --git a/src/features/form/containers/RepeatingGroupsEditContainer.test.tsx b/src/features/form/containers/RepeatingGroupsEditContainer.test.tsx index e390199cfd..abc6a6c1ff 100644 --- a/src/features/form/containers/RepeatingGroupsEditContainer.test.tsx +++ b/src/features/form/containers/RepeatingGroupsEditContainer.test.tsx @@ -85,8 +85,12 @@ describe('RepeatingGroupsEditContainer', () => { const layout: ILayout = [multiPageGroup, ...components]; const repeatingGroupIndex = 3; - const repeatingGroupDeepCopyComponents: Array> = - createRepeatingGroupComponents(multiPageGroup, components, repeatingGroupIndex, textResources); + const repeatingGroupDeepCopyComponents = createRepeatingGroupComponents( + multiPageGroup, + components, + repeatingGroupIndex, + textResources, + ); it('calls setEditIndex when save and open next is pressed when edit.saveAndNextButton is true', async () => { const setEditIndex = jest.fn(); diff --git a/src/features/form/containers/RepeatingGroupsEditContainer.tsx b/src/features/form/containers/RepeatingGroupsEditContainer.tsx index 5dd4e8d029..b53c7b7f3d 100644 --- a/src/features/form/containers/RepeatingGroupsEditContainer.tsx +++ b/src/features/form/containers/RepeatingGroupsEditContainer.tsx @@ -11,7 +11,7 @@ import theme from 'src/theme/altinnStudioTheme'; import { renderGenericComponent } from 'src/utils/layout'; import { getLanguageFromKey, getTextResourceByKey } from 'src/utils/sharedUtils'; import type { ILayoutGroup } from 'src/layout/Group/types'; -import type { ILayout, ILayoutComponent } from 'src/layout/layout'; +import type { ComponentInGroup, ILayout } from 'src/layout/layout'; import type { ITextResource } from 'src/types'; import type { ILanguage } from 'src/types/shared'; @@ -19,7 +19,7 @@ export interface IRepeatingGroupsEditContainer { id: string; className?: string; container: ILayoutGroup; - repeatingGroupDeepCopyComponents: (ILayoutComponent | ILayoutGroup)[][]; + repeatingGroupDeepCopyComponents: ComponentInGroup[][]; language: ILanguage; textResources: ITextResource[]; layout: ILayout | null; @@ -168,7 +168,7 @@ export function RepeatingGroupsEditContainer({ item={true} spacing={3} > - {repeatingGroupDeepCopyComponents[editIndex]?.map((component: ILayoutComponent) => { + {repeatingGroupDeepCopyComponents[editIndex]?.map((component) => { if ( group.edit?.multiPage && typeof multiPageIndex === 'number' && diff --git a/src/features/form/containers/RepeatingGroupsTable.test.tsx b/src/features/form/containers/RepeatingGroupsTable.test.tsx index 874032affd..276ac27c7f 100644 --- a/src/features/form/containers/RepeatingGroupsTable.test.tsx +++ b/src/features/form/containers/RepeatingGroupsTable.test.tsx @@ -12,7 +12,7 @@ import type { IRepeatingGroupTableProps } from 'src/features/form/containers/Rep import type { IFormData } from 'src/features/form/data'; import type { ILayoutState } from 'src/features/form/layout/formLayoutSlice'; import type { ILayoutGroup } from 'src/layout/Group/types'; -import type { ILayoutComponent, ISelectionComponentProps } from 'src/layout/layout'; +import type { ComponentInGroup, ILayoutComponent, ISelectionComponentProps } from 'src/layout/layout'; import type { IAttachments } from 'src/shared/resources/attachments'; import type { IOption, ITextResource } from 'src/types'; import type { ILanguage } from 'src/types/shared'; @@ -21,7 +21,7 @@ import type { ILanguage } from 'src/types/shared'; const user = userEvent.setup(); -const getLayout = (group: ILayoutGroup, components: ILayoutComponent[]) => { +const getLayout = (group: ILayoutGroup, components: (ILayoutComponent | ComponentInGroup)[]) => { const layout: ILayoutState = { layouts: { FormLayout: [group, ...components], @@ -65,7 +65,7 @@ describe('RepeatingGroupTable', () => { const textResources: ITextResource[] = [{ id: 'option.label', value: 'Value to be shown' }]; const attachments: IAttachments = {}; const options: IOption[] = [{ value: 'option.value', label: 'option.label' }]; - const components: ILayoutComponent[] = [ + const components: ComponentInGroup[] = [ { id: 'field1', type: 'Input', @@ -127,8 +127,12 @@ describe('RepeatingGroupTable', () => { }; const repeatingGroupIndex = 3; - const repeatingGroupDeepCopyComponents: Array> = - createRepeatingGroupComponents(group, components, repeatingGroupIndex, textResources); + const repeatingGroupDeepCopyComponents = createRepeatingGroupComponents( + group, + components, + repeatingGroupIndex, + textResources, + ); it('should render table header when table has entries', () => { const container = render(); @@ -150,8 +154,12 @@ describe('RepeatingGroupTable', () => { edit: { alertOnDelete: true }, }); const layout: ILayoutState = getLayout(group, components); - const repeatingGroupDeepCopyComponents: Array> = - createRepeatingGroupComponents(group, components, repeatingGroupIndex, textResources); + const repeatingGroupDeepCopyComponents = createRepeatingGroupComponents( + group, + components, + repeatingGroupIndex, + textResources, + ); if (!layout.layouts) { return; diff --git a/src/features/form/containers/formUtils.ts b/src/features/form/containers/formUtils.ts index ab56be692a..f71600227e 100644 --- a/src/features/form/containers/formUtils.ts +++ b/src/features/form/containers/formUtils.ts @@ -1,5 +1,5 @@ import type { ILayoutGroup } from 'src/layout/Group/types'; -import type { ILayout, ILayoutComponentOrGroup } from 'src/layout/layout'; +import type { ComponentInGroup, ILayout } from 'src/layout/layout'; export const mapGroupComponents = ({ children, edit }: ILayoutGroup, layout: ILayout | undefined | null) => children @@ -7,4 +7,4 @@ export const mapGroupComponents = ({ children, edit }: ILayoutGroup, layout: ILa const childId = (edit?.multiPage && child.split(':')[1]) || child; return layout && layout.find((c) => c.id === childId); }) - .filter((c) => c !== undefined && c !== null) as ILayoutComponentOrGroup[]; + .filter((c) => c !== undefined && c !== null) as ComponentInGroup[]; diff --git a/src/layout/Address/index.tsx b/src/layout/Address/index.tsx new file mode 100644 index 0000000000..a2e1aaf4d7 --- /dev/null +++ b/src/layout/Address/index.tsx @@ -0,0 +1,15 @@ +import React from 'react'; + +import { AddressComponent } from 'src/layout/Address/AddressComponent'; +import { LayoutComponent } from 'src/layout/LayoutComponent'; +import type { PropsFromGenericComponent } from 'src/layout'; + +export class Address extends LayoutComponent<'AddressComponent'> { + render(props: PropsFromGenericComponent<'AddressComponent'>): JSX.Element | null { + return ; + } + + renderWithLabel(): boolean { + return false; + } +} diff --git a/src/layout/AttachmentList/index.tsx b/src/layout/AttachmentList/index.tsx new file mode 100644 index 0000000000..fc92bd5835 --- /dev/null +++ b/src/layout/AttachmentList/index.tsx @@ -0,0 +1,15 @@ +import React from 'react'; + +import { AttachmentListComponent } from 'src/layout/AttachmentList/AttachmentListComponent'; +import { LayoutComponent } from 'src/layout/LayoutComponent'; +import type { PropsFromGenericComponent } from 'src/layout'; + +export class AttachmentList extends LayoutComponent<'AttachmentList'> { + render(props: PropsFromGenericComponent<'AttachmentList'>): JSX.Element | null { + return ; + } + + renderWithLabel(): boolean { + return false; + } +} diff --git a/src/layout/Button/ButtonComponent.tsx b/src/layout/Button/ButtonComponent.tsx index 399705f027..2aaf664583 100644 --- a/src/layout/Button/ButtonComponent.tsx +++ b/src/layout/Button/ButtonComponent.tsx @@ -8,17 +8,10 @@ import { SaveButton } from 'src/layout/Button/SaveButton'; import { SubmitButton } from 'src/layout/Button/SubmitButton'; import { ProcessActions } from 'src/shared/resources/process/processSlice'; import { getLanguageFromKey } from 'src/utils/sharedUtils'; -import type { IComponentProps } from 'src/layout'; -import type { ButtonMode } from 'src/layout/Button/getComponentFromMode'; -import type { ILayoutCompButton } from 'src/layout/Button/types'; +import type { PropsFromGenericComponent } from 'src/layout'; import type { IAltinnWindow } from 'src/types'; -export interface IButtonProvidedProps extends IComponentProps, ILayoutCompButton { - disabled: boolean; - id: string; - mode?: ButtonMode; - taskId?: string; -} +export type IButtonProvidedProps = PropsFromGenericComponent<'Button'>; export const ButtonComponent = ({ mode, ...props }: IButtonProvidedProps) => { const dispatch = useAppDispatch(); @@ -29,6 +22,10 @@ export const ButtonComponent = ({ mode, ...props }: IButtonProvidedProps) => { const currentTaskType = useAppSelector((state) => state.instanceData.instance?.process.currentTask?.altinnTaskType); if (mode && !(mode === 'save' || mode === 'submit')) { const GenericButton = getComponentFromMode(mode); + if (!GenericButton) { + return null; + } + return (
diff --git a/src/layout/Button/GoToTaskButton.test.tsx b/src/layout/Button/GoToTaskButton.test.tsx index 8a2dfb77cd..1987f34f8d 100644 --- a/src/layout/Button/GoToTaskButton.test.tsx +++ b/src/layout/Button/GoToTaskButton.test.tsx @@ -6,23 +6,32 @@ import userEvent from '@testing-library/user-event'; import { getInitialStateMock } from 'src/__mocks__/mocks'; import { GoToTaskButton } from 'src/layout/Button/GoToTaskButton'; import { setupStore } from 'src/store'; -import { renderWithProviders } from 'src/testUtils'; -import type { Props as GoToTaskButtonProps } from 'src/layout/Button/GoToTaskButton'; +import { mockComponentProps, renderWithProviders } from 'src/testUtils'; +import type { IButtonProvidedProps } from 'src/layout/Button/ButtonComponent'; -const render = ({ props = {}, dispatch = jest.fn() } = {}) => { - const allProps = { - id: 'go-to-task-button', - ...props, - } as GoToTaskButtonProps; +interface RenderProps { + props: Partial; + dispatch: (...args: any[]) => any; +} + +const render = ({ props, dispatch }: RenderProps) => { const stateMock = getInitialStateMock(); stateMock.process.availableNextTasks = ['a', 'b']; const store = setupStore(stateMock); store.dispatch = dispatch; - renderWithProviders(Go to task, { - store, - }); + renderWithProviders( + + Go to task + , + { + store, + }, + ); }; describe('GoToTaskButton', () => { diff --git a/src/layout/Button/GoToTaskButton.tsx b/src/layout/Button/GoToTaskButton.tsx index f489e96977..89e8328190 100644 --- a/src/layout/Button/GoToTaskButton.tsx +++ b/src/layout/Button/GoToTaskButton.tsx @@ -6,14 +6,12 @@ import { useAppDispatch, useAppSelector } from 'src/common/hooks'; import { WrappedButton } from 'src/layout/Button/WrappedButton'; import { ProcessActions } from 'src/shared/resources/process/processSlice'; import { ProcessTaskType } from 'src/types'; -import type { ButtonProps } from 'src/layout/Button/WrappedButton'; +import type { IButtonProvidedProps } from 'src/layout/Button/ButtonComponent'; -export type Props = Omit & { taskId: string }; - -export const GoToTaskButton = ({ children, taskId, ...props }: Props) => { +export const GoToTaskButton = ({ children, taskId, ...props }: React.PropsWithChildren) => { const dispatch = useAppDispatch(); const availableProcessTasks = useAppSelector((state) => state.process.availableNextTasks); - const canGoToTask = availableProcessTasks && availableProcessTasks.includes(taskId); + const canGoToTask = availableProcessTasks && availableProcessTasks.includes(taskId || ''); const navigateToTask = () => { if (canGoToTask) { dispatch( diff --git a/src/layout/Button/getComponentFromMode.ts b/src/layout/Button/getComponentFromMode.ts index 20ca1c67c7..277ce03384 100644 --- a/src/layout/Button/getComponentFromMode.ts +++ b/src/layout/Button/getComponentFromMode.ts @@ -1,11 +1,17 @@ +import type React from 'react'; + import { GoToTaskButton } from 'src/layout/Button/GoToTaskButton'; import { InstantiationButton } from 'src/layout/InstantiationButton/InstantiationButton'; -export type ButtonMode = 'submit' | 'save' | 'go-to-task' | 'instantiate'; +import type { IButtonProvidedProps } from 'src/layout/Button/ButtonComponent'; +import type { ButtonMode } from 'src/layout/Button/types'; -const buttons = { +const buttons: { [key in ButtonMode]: React.FC> | null } = { + save: null, + submit: null, 'go-to-task': GoToTaskButton, instantiate: InstantiationButton, }; + export const getComponentFromMode = (mode: ButtonMode) => { return buttons[mode]; }; diff --git a/src/layout/Button/index.tsx b/src/layout/Button/index.tsx new file mode 100644 index 0000000000..725afb6fdd --- /dev/null +++ b/src/layout/Button/index.tsx @@ -0,0 +1,15 @@ +import React from 'react'; + +import { ButtonComponent } from 'src/layout/Button/ButtonComponent'; +import { LayoutComponent } from 'src/layout/LayoutComponent'; +import type { PropsFromGenericComponent } from 'src/layout'; + +export class Button extends LayoutComponent<'Button'> { + render(props: PropsFromGenericComponent<'Button'>): JSX.Element | null { + return ; + } + + renderWithLabel(): boolean { + return false; + } +} diff --git a/src/layout/Button/types.d.ts b/src/layout/Button/types.d.ts index 44f9eb4d62..cd8fd62e8c 100644 --- a/src/layout/Button/types.d.ts +++ b/src/layout/Button/types.d.ts @@ -1,3 +1,13 @@ import type { ILayoutCompBase } from 'src/layout/layout'; +import type { IMapping } from 'src/types'; -export type ILayoutCompButton = ILayoutCompBase<'Button'>; +export type ButtonMode = 'submit' | 'save' | 'go-to-task' | 'instantiate'; + +export interface ILayoutCompButton extends ILayoutCompBase<'Button'> { + mode?: ButtonMode; + + taskId?: string; // Required for go-to-task + + busyWithId?: string; + mapping?: IMapping; +} diff --git a/src/layout/Checkboxes/CheckboxesContainerComponent.test.tsx b/src/layout/Checkboxes/CheckboxesContainerComponent.test.tsx index f114ebe9bf..f0cc491dc6 100644 --- a/src/layout/Checkboxes/CheckboxesContainerComponent.test.tsx +++ b/src/layout/Checkboxes/CheckboxesContainerComponent.test.tsx @@ -35,7 +35,6 @@ const render = (props: Partial = {}, customState: Prelo options: [], optionsId: 'countries', preselectedOptionIndex: undefined, - validationMessages: {}, legend: () => legend, handleDataChange: jest.fn(), getTextResource: (value) => value, diff --git a/src/layout/Checkboxes/CheckboxesContainerComponent.tsx b/src/layout/Checkboxes/CheckboxesContainerComponent.tsx index 7e05b8a20a..e45eb03e8b 100644 --- a/src/layout/Checkboxes/CheckboxesContainerComponent.tsx +++ b/src/layout/Checkboxes/CheckboxesContainerComponent.tsx @@ -13,13 +13,10 @@ import { useDelayedSavedState } from 'src/components/hooks/useDelayedSavedState' import { AltinnSpinner } from 'src/components/shared'; import { shouldUseRowLayout } from 'src/utils/layout'; import { getOptionLookupKey } from 'src/utils/options'; -import { renderValidationMessagesForComponent } from 'src/utils/render'; import type { PropsFromGenericComponent } from 'src/layout'; -import type { IComponentValidations, IOption } from 'src/types'; +import type { IOption } from 'src/types'; -export interface ICheckboxContainerProps extends PropsFromGenericComponent<'Checkboxes'> { - validationMessages: IComponentValidations; -} +export type ICheckboxContainerProps = PropsFromGenericComponent<'Checkboxes'>; interface IStyledCheckboxProps extends CheckboxProps { label: string; @@ -92,7 +89,6 @@ export const CheckboxContainerComponent = ({ readOnly, getTextResourceAsString, getTextResource, - validationMessages, mapping, source, }: ICheckboxContainerProps) => { @@ -177,28 +173,23 @@ export const CheckboxContainerComponent = ({ ) : ( <> {calculatedOptions.map((option, index) => ( - - - } - label={getTextResource(option.label)} - /> - {validationMessages && - isOptionSelected(option.value) && - renderValidationMessagesForComponent(validationMessages.simpleBinding, id)} - + + } + label={getTextResource(option.label)} + /> ))} )} diff --git a/src/layout/Checkboxes/index.tsx b/src/layout/Checkboxes/index.tsx new file mode 100644 index 0000000000..56126d2046 --- /dev/null +++ b/src/layout/Checkboxes/index.tsx @@ -0,0 +1,15 @@ +import React from 'react'; + +import { CheckboxContainerComponent } from 'src/layout/Checkboxes/CheckboxesContainerComponent'; +import { LayoutComponent } from 'src/layout/LayoutComponent'; +import type { PropsFromGenericComponent } from 'src/layout'; + +export class Checkboxes extends LayoutComponent<'Checkboxes'> { + render(props: PropsFromGenericComponent<'Checkboxes'>): JSX.Element | null { + return ; + } + + renderWithLabel(): boolean { + return false; + } +} diff --git a/src/layout/Custom/index.tsx b/src/layout/Custom/index.tsx new file mode 100644 index 0000000000..f6e2f6702c --- /dev/null +++ b/src/layout/Custom/index.tsx @@ -0,0 +1,15 @@ +import React from 'react'; + +import { CustomWebComponent } from 'src/layout/Custom/CustomWebComponent'; +import { LayoutComponent } from 'src/layout/LayoutComponent'; +import type { PropsFromGenericComponent } from 'src/layout'; + +export class Custom extends LayoutComponent<'Custom'> { + render(props: PropsFromGenericComponent<'Custom'>): JSX.Element | null { + return ; + } + + renderWithLabel(): boolean { + return false; + } +} diff --git a/src/layout/Datepicker/index.tsx b/src/layout/Datepicker/index.tsx new file mode 100644 index 0000000000..2100aedf74 --- /dev/null +++ b/src/layout/Datepicker/index.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +import { DatepickerComponent } from 'src/layout/Datepicker/DatepickerComponent'; +import { LayoutComponent } from 'src/layout/LayoutComponent'; +import type { PropsFromGenericComponent } from 'src/layout'; + +export class Datepicker extends LayoutComponent<'Datepicker'> { + render(props: PropsFromGenericComponent<'Datepicker'>): JSX.Element | null { + return ; + } +} diff --git a/src/layout/Dropdown/index.tsx b/src/layout/Dropdown/index.tsx new file mode 100644 index 0000000000..e54e7fb842 --- /dev/null +++ b/src/layout/Dropdown/index.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +import { DropdownComponent } from 'src/layout/Dropdown/DropdownComponent'; +import { LayoutComponent } from 'src/layout/LayoutComponent'; +import type { PropsFromGenericComponent } from 'src/layout'; + +export class Dropdown extends LayoutComponent<'Dropdown'> { + render(props: PropsFromGenericComponent<'Dropdown'>): JSX.Element | null { + return ; + } +} diff --git a/src/layout/FileUpload/index.tsx b/src/layout/FileUpload/index.tsx new file mode 100644 index 0000000000..ab2dddbd80 --- /dev/null +++ b/src/layout/FileUpload/index.tsx @@ -0,0 +1,15 @@ +import React from 'react'; + +import { FileUploadComponent } from 'src/layout/FileUpload/FileUploadComponent'; +import { LayoutComponent } from 'src/layout/LayoutComponent'; +import type { PropsFromGenericComponent } from 'src/layout'; + +export class FileUpload extends LayoutComponent<'FileUpload'> { + render(props: PropsFromGenericComponent<'FileUpload'>): JSX.Element | null { + return ; + } + + renderDefaultValidations(): boolean { + return false; + } +} diff --git a/src/layout/FileUploadWithTag/index.tsx b/src/layout/FileUploadWithTag/index.tsx new file mode 100644 index 0000000000..f73828bf87 --- /dev/null +++ b/src/layout/FileUploadWithTag/index.tsx @@ -0,0 +1,15 @@ +import React from 'react'; + +import { FileUploadWithTagComponent } from 'src/layout/FileUploadWithTag/FileUploadWithTagComponent'; +import { LayoutComponent } from 'src/layout/LayoutComponent'; +import type { PropsFromGenericComponent } from 'src/layout'; + +export class FileUploadWithTag extends LayoutComponent<'FileUploadWithTag'> { + render(props: PropsFromGenericComponent<'FileUploadWithTag'>): JSX.Element | null { + return ; + } + + renderDefaultValidations(): boolean { + return false; + } +} diff --git a/src/layout/GenericComponent.tsx b/src/layout/GenericComponent.tsx index ab0d838a56..414cb6707d 100644 --- a/src/layout/GenericComponent.tsx +++ b/src/layout/GenericComponent.tsx @@ -12,11 +12,11 @@ import Legend from 'src/features/form/components/Legend'; import { FormDataActions } from 'src/features/form/data/formDataSlice'; import { FormLayoutActions } from 'src/features/form/layout/formLayoutSlice'; import components, { FormComponentContext } from 'src/layout/index'; +import { getLayoutComponentObject } from 'src/layout/LayoutComponent'; import { makeGetFocus, makeGetHidden } from 'src/selectors/getLayoutData'; -import { LayoutStyle, Triggers } from 'src/types'; +import { Triggers } from 'src/types'; import { componentHasValidationMessages, - componentValidationsHandledByGenericComponent, getFormDataForComponent, getTextResource, gridBreakpoints, @@ -29,17 +29,15 @@ import type { ExprResolved } from 'src/features/expressions/types'; import type { ISingleFieldValidation } from 'src/features/form/data/formDataTypes'; import type { IComponentProps, IFormComponentContext, PropsFromGenericComponent } from 'src/layout/index'; import type { - ComponentExceptGroup, + ComponentExceptGroupAndSummary, ComponentTypes, IGridStyling, ILayoutCompBase, ILayoutComponent, } from 'src/layout/layout'; -import type { IComponentValidations, ILabelSettings } from 'src/types'; -import type { ILanguage } from 'src/types/shared'; +import type { ILabelSettings, LayoutStyle } from 'src/types'; export interface IGenericComponentProps { - componentValidations?: IComponentValidations; labelSettings?: ILabelSettings; layout?: LayoutStyle; groupContainerId?: string; @@ -97,7 +95,9 @@ const useStyles = makeStyles((theme) => ({ }, })); -export function GenericComponent(_props: IActualGenericComponentProps) { +export function GenericComponent( + _props: IActualGenericComponentProps, +) { const props = useExpressionsForComponent(_props as ILayoutComponent) as ExprResolved< IActualGenericComponentProps > & { @@ -201,27 +201,8 @@ export function GenericComponent(_props: IAct ); }; - const getValidationsForInternalHandling = () => { - if ( - props.type === 'AddressComponent' || - props.type === 'Datepicker' || - props.type === 'FileUpload' || - props.type === 'FileUploadWithTag' || - (props.type === 'Likert' && props.layout === LayoutStyle.Table) - ) { - return componentValidations; - } - return null; - }; - - // some components handle their validations internally (i.e merge with internal validation state) - const internalComponentValidations = getValidationsForInternalHandling(); - if (internalComponentValidations !== null) { - passThroughProps.componentValidations = internalComponentValidations; - } - - const RenderComponent = components[props.type as keyof typeof components]; - if (!RenderComponent) { + const layoutComponent = getLayoutComponentObject(_props.type); + if (!layoutComponent) { return (
Unknown component type: {props.type} @@ -231,16 +212,20 @@ export function GenericComponent(_props: IAct ); } - const RenderLabel = () => { - return ( - - ); - }; + const RenderComponent = layoutComponent.render; + + const RenderLabel = () => ( +