From 0666d9afd839895619c8e81ecd6bb28f15a503c9 Mon Sep 17 00:00:00 2001 From: Adam Haeger Date: Fri, 15 May 2026 12:38:58 +0200 Subject: [PATCH 01/13] moved Datepicker to lib --- libs/form-component/package.json | 12 +++- .../Datepicker/Calendar.module.css | 0 .../Datepicker/DatePickerCalendar.tsx | 5 +- .../Datepicker/DatePickerInput.tsx | 14 ++++- .../Datepicker/Datepicker.stories.tsx | 58 +++++++++++++++++++ .../app-components/Datepicker/Datepicker.tsx | 18 +++--- .../Datepicker/DatepickerDialog.tsx | 6 +- .../src/app-components/Datepicker/index.ts | 28 +++++++++ .../Datepicker/utils/dateHelpers.test.ts | 15 +++-- .../Datepicker/utils/dateHelpers.ts | 26 +++++++-- .../Datepicker/utils/dateLocales.ts | 0 .../src/app-components/Flex/Flex.module.css | 0 .../src/app-components/Flex/Flex.tsx | 13 ++++- .../src/app-components/Flex/index.ts | 2 + .../src/app-components/hooks/index.ts | 7 +++ .../app-components/hooks/useDeviceWidths.ts | 3 +- .../src/app-components/index.ts | 3 + src/App/frontend/monorepo-changed-paths.txt | 3 +- .../DynamicForm/DynamicForm.tsx | 3 +- .../src/app-components/Label/Fieldset.tsx | 2 +- .../src/app-components/Label/Label.tsx | 2 +- .../app-components/Pagination/Pagination.tsx | 2 +- .../src/app-components/Panel/Panel.tsx | 2 +- .../src/components/AltinnCollapsible.tsx | 2 +- .../frontend/src/components/altinnParty.tsx | 2 +- src/App/frontend/src/components/form/Form.tsx | 3 +- .../frontend/src/components/label/Label.tsx | 2 +- .../src/components/message/ErrorReport.tsx | 2 +- .../components/molecules/AltinnSubstatus.tsx | 2 +- .../src/components/presentation/Header.tsx | 2 +- .../components/presentation/Presentation.tsx | 2 +- .../components/wrappers/ProcessWrapper.tsx | 2 +- .../containers/InstantiationErrorPage.tsx | 3 +- .../instantiate/containers/PartySelection.tsx | 2 +- .../src/features/pdf/PdfFromLayout.tsx | 2 +- .../src/features/receipt/ReceiptContainer.tsx | 2 +- .../src/layout/Accordion/Accordion.tsx | 2 +- .../src/layout/Address/AddressComponent.tsx | 3 +- .../ButtonGroup/ButtonGroupComponent.tsx | 3 +- src/App/frontend/src/layout/Cards/Cards.tsx | 3 +- .../Checkboxes/MultipleChoiceSummary.tsx | 2 +- .../src/layout/ComponentStructureWrapper.tsx | 3 +- .../Datepicker/DatepickerComponent.test.tsx | 6 +- .../layout/Datepicker/DatepickerComponent.tsx | 5 +- .../Datepicker/DropdownCaption.module.css | 38 ++++++++++++ .../src/layout/Datepicker/DropdownCaption.tsx | 5 +- .../frontend/src/layout/Datepicker/index.tsx | 3 +- .../Datepicker/useDatepickerValidation.ts | 2 +- .../FileUploadWithTag/EditWindowComponent.tsx | 2 +- .../frontend/src/layout/GenericComponent.tsx | 2 +- .../src/layout/Group/GroupSummary.tsx | 2 +- .../src/layout/Image/ImageComponent.tsx | 3 +- .../InstanceInformationComponent.tsx | 2 +- .../NavigationBar/NavigationBarComponent.tsx | 2 +- .../Container/RepeatingGroupContainer.tsx | 2 +- .../RepeatingGroupsEditContainer.tsx | 2 +- .../Summary2/RepeatingGroupSummary.tsx | 2 +- .../Table/RepeatingGroupTableRow.tsx | 2 +- .../src/layout/Subform/SubformComponent.tsx | 2 +- .../Summary/SubformSummaryComponent2.tsx | 2 +- .../Subform/Summary/SubformSummaryTable.tsx | 2 +- .../src/layout/Summary/SummaryComponent.tsx | 2 +- .../LayoutSetSummaryAccordion.tsx | 3 +- .../SummaryComponent2/ComponentSummary.tsx | 2 +- .../SummaryComponent2/PageSummary.tsx | 3 +- src/App/frontend/src/layout/Tabs/Tabs.tsx | 2 +- .../frontend/src/layout/Tabs/TabsSummary.tsx | 2 +- .../layout/TimePicker/TimePickerComponent.tsx | 3 +- src/App/frontend/src/utils/dateUtils.test.ts | 3 +- 69 files changed, 279 insertions(+), 95 deletions(-) rename {src/App/frontend => libs/form-component}/src/app-components/Datepicker/Calendar.module.css (100%) rename {src/App/frontend => libs/form-component}/src/app-components/Datepicker/DatePickerCalendar.tsx (90%) rename {src/App/frontend => libs/form-component}/src/app-components/Datepicker/DatePickerInput.tsx (91%) create mode 100644 libs/form-component/src/app-components/Datepicker/Datepicker.stories.tsx rename {src/App/frontend => libs/form-component}/src/app-components/Datepicker/Datepicker.tsx (82%) rename {src/App/frontend => libs/form-component}/src/app-components/Datepicker/DatepickerDialog.tsx (91%) create mode 100644 libs/form-component/src/app-components/Datepicker/index.ts rename {src/App/frontend => libs/form-component}/src/app-components/Datepicker/utils/dateHelpers.test.ts (94%) rename {src/App/frontend => libs/form-component}/src/app-components/Datepicker/utils/dateHelpers.ts (93%) rename {src/App/frontend => libs/form-component}/src/app-components/Datepicker/utils/dateLocales.ts (100%) rename {src/App/frontend => libs/form-component}/src/app-components/Flex/Flex.module.css (100%) rename {src/App/frontend => libs/form-component}/src/app-components/Flex/Flex.tsx (89%) create mode 100644 libs/form-component/src/app-components/Flex/index.ts create mode 100644 libs/form-component/src/app-components/hooks/index.ts rename {src/App/frontend => libs/form-component}/src/app-components/hooks/useDeviceWidths.ts (90%) create mode 100644 src/App/frontend/src/layout/Datepicker/DropdownCaption.module.css diff --git a/libs/form-component/package.json b/libs/form-component/package.json index eb8b68d694a..6acda3ba5f0 100644 --- a/libs/form-component/package.json +++ b/libs/form-component/package.json @@ -18,13 +18,19 @@ }, "peerDependencies": { "@digdir/designsystemet-react": "^1.11.1", + "@navikt/aksel-icons": "^8.9.0", + "classnames": "^2.5.1", + "date-fns": "^4.1.0", "react": "^19.0.0", - "react-dom": "^19.0.0" + "react-day-picker": "^9.14.0", + "react-dom": "^19.0.0", + "react-number-format": "^5.4.5" }, "devDependencies": { "@chromatic-com/storybook": "^5.0.1", "@digdir/designsystemet-css": "1.11.1", "@digdir/designsystemet-react": "1.11.1", + "@navikt/aksel-icons": "8.9.0", "@storybook/addon-docs": "10.2.16", "@storybook/addon-links": "10.2.16", "@storybook/builder-vite": "10.2.16", @@ -35,7 +41,11 @@ "@types/react": "19.2.14", "@types/react-dom": "19.2.3", "@vitejs/plugin-react": "^5.0.0", + "classnames": "2.5.1", + "date-fns": "4.1.0", "jsdom": "^26.1.0", + "react-day-picker": "9.14.0", + "react-number-format": "5.4.5", "storybook": "^10.2.11", "typescript": "5.9.3", "typescript-plugin-css-modules": "5.2.0", diff --git a/src/App/frontend/src/app-components/Datepicker/Calendar.module.css b/libs/form-component/src/app-components/Datepicker/Calendar.module.css similarity index 100% rename from src/App/frontend/src/app-components/Datepicker/Calendar.module.css rename to libs/form-component/src/app-components/Datepicker/Calendar.module.css diff --git a/src/App/frontend/src/app-components/Datepicker/DatePickerCalendar.tsx b/libs/form-component/src/app-components/Datepicker/DatePickerCalendar.tsx similarity index 90% rename from src/App/frontend/src/app-components/Datepicker/DatePickerCalendar.tsx rename to libs/form-component/src/app-components/Datepicker/DatePickerCalendar.tsx index 46de342bbe9..93e3147e85f 100644 --- a/src/App/frontend/src/app-components/Datepicker/DatePickerCalendar.tsx +++ b/libs/form-component/src/app-components/Datepicker/DatePickerCalendar.tsx @@ -1,9 +1,8 @@ -import React from 'react'; import { DayPicker } from 'react-day-picker'; import type { Matcher, MonthCaption } from 'react-day-picker'; -import styles from 'src/app-components/Datepicker/Calendar.module.css'; -import { getLocale } from 'src/app-components/Datepicker/utils/dateHelpers'; +import styles from './Calendar.module.css'; +import { getLocale } from './utils/dateHelpers'; export interface CalendarDialogProps { id: string; diff --git a/src/App/frontend/src/app-components/Datepicker/DatePickerInput.tsx b/libs/form-component/src/app-components/Datepicker/DatePickerInput.tsx similarity index 91% rename from src/App/frontend/src/app-components/Datepicker/DatePickerInput.tsx rename to libs/form-component/src/app-components/Datepicker/DatePickerInput.tsx index 34398598386..bb096028057 100644 --- a/src/App/frontend/src/app-components/Datepicker/DatePickerInput.tsx +++ b/libs/form-component/src/app-components/Datepicker/DatePickerInput.tsx @@ -5,14 +5,14 @@ import type { Ref } from 'react'; import { Textfield } from '@digdir/designsystemet-react'; import { format, isValid } from 'date-fns'; -import styles from 'src/app-components/Datepicker/Calendar.module.css'; +import styles from './Calendar.module.css'; import { dateFormatCanBeNumericInReactPatternFormat, getFormatAsPatternFormat, getSaveFormattedDateString, strictParseFormat, strictParseISO, -} from 'src/app-components/Datepicker/utils/dateHelpers'; +} from './utils/dateHelpers'; export interface DatePickerInputProps { id: string; @@ -25,7 +25,15 @@ export interface DatePickerInputProps { } function DatePickerInputRef( - { id, value, datepickerFormat, timeStamp, onValueChange, readOnly, autoComplete }: DatePickerInputProps, + { + id, + value, + datepickerFormat, + timeStamp, + onValueChange, + readOnly, + autoComplete, + }: DatePickerInputProps, ref: Ref, ) { const dateValue = strictParseISO(value); diff --git a/libs/form-component/src/app-components/Datepicker/Datepicker.stories.tsx b/libs/form-component/src/app-components/Datepicker/Datepicker.stories.tsx new file mode 100644 index 00000000000..9d4a6a06f04 --- /dev/null +++ b/libs/form-component/src/app-components/Datepicker/Datepicker.stories.tsx @@ -0,0 +1,58 @@ +import { useState } from 'react'; +import type { MonthCaptionProps } from 'react-day-picker'; + +import type { Meta, StoryObj } from '@storybook/react-vite'; + +import 'react-day-picker/style.css'; + +import { DatePickerControl } from './Datepicker'; + +const NoopDropdownCaption = ({ calendarMonth }: MonthCaptionProps) => ( +
+ {calendarMonth.date.toLocaleString('en', { month: 'long', year: 'numeric' })} +
+); + +const meta = { + title: 'AppComponents/DatePickerControl', + component: DatePickerControl, +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +const Wrapper = (args: React.ComponentProps) => { + const [value, setValue] = useState(args.value); + return ; +}; + +export const Preview: Story = { + args: { + id: 'datepicker-preview', + value: '2025-03-15', + dateFormat: 'dd.MM.yyyy', + locale: 'nb', + buttonAriaLabel: 'Open date picker', + calendarIconTitle: 'Calendar', + DropdownCaption: NoopDropdownCaption, + }, + render: (args) => , +}; + +export const ReadOnly: Story = { + args: { + ...Preview.args, + readOnly: true, + }, + render: (args) => , +}; + +export const WithMinMax: Story = { + args: { + ...Preview.args, + minDate: new Date('2025-01-01'), + maxDate: new Date('2025-12-31'), + }, + render: (args) => , +}; diff --git a/src/App/frontend/src/app-components/Datepicker/Datepicker.tsx b/libs/form-component/src/app-components/Datepicker/Datepicker.tsx similarity index 82% rename from src/App/frontend/src/app-components/Datepicker/Datepicker.tsx rename to libs/form-component/src/app-components/Datepicker/Datepicker.tsx index 98cc9b01505..1118d431779 100644 --- a/src/App/frontend/src/app-components/Datepicker/Datepicker.tsx +++ b/libs/form-component/src/app-components/Datepicker/Datepicker.tsx @@ -4,12 +4,12 @@ import type { MonthCaption } from 'react-day-picker'; import { CalendarIcon } from '@navikt/aksel-icons'; import { isValid as isValidDate } from 'date-fns'; -import styles from 'src/app-components/Datepicker/Calendar.module.css'; -import { DatePickerCalendar } from 'src/app-components/Datepicker/DatePickerCalendar'; -import { DatePickerDialog } from 'src/app-components/Datepicker/DatepickerDialog'; -import { DatePickerInput } from 'src/app-components/Datepicker/DatePickerInput'; -import { getSaveFormattedDateString } from 'src/app-components/Datepicker/utils/dateHelpers'; -import { Flex } from 'src/app-components/Flex/Flex'; +import { Flex } from '../Flex/Flex'; +import styles from './Calendar.module.css'; +import { DatePickerCalendar } from './DatePickerCalendar'; +import { DatePickerDialog } from './DatepickerDialog'; +import { DatePickerInput } from './DatePickerInput'; +import { getSaveFormattedDateString } from './utils/dateHelpers'; export type DatePickerControlProps = { id: string; @@ -60,11 +60,7 @@ export const DatePickerControl: React.FC = ({ }; return ( - +
void) | null>(null); diff --git a/libs/form-component/src/app-components/Datepicker/index.ts b/libs/form-component/src/app-components/Datepicker/index.ts new file mode 100644 index 00000000000..a8621bf7444 --- /dev/null +++ b/libs/form-component/src/app-components/Datepicker/index.ts @@ -0,0 +1,28 @@ +export { DatePickerControl } from './Datepicker'; +export type { DatePickerControlProps } from './Datepicker'; +export { DatePickerCalendar } from './DatePickerCalendar'; +export type { CalendarDialogProps } from './DatePickerCalendar'; +export { DatePickerInput } from './DatePickerInput'; +export type { DatePickerInputProps } from './DatePickerInput'; +export { DatePickerDialog, useDatePickerClose } from './DatepickerDialog'; +export { + DateFlags, + DatepickerMinDateDefault, + DatepickerMaxDateDefault, + DatepickerFormatDefault, + PrettyDateAndTime, + getDateFormat, + getSaveFormattedDateString, + getDateConstraint, + formatISOString, + isDate, + getLocale, + getDateLib, + strictParseISO, + strictParseFormat, + dateFormatCanBeNumericInReactPatternFormat, + getFormatAsPatternFormat, + getMonths, + getYears, +} from './utils/dateHelpers'; +export type { Token } from './utils/dateHelpers'; diff --git a/src/App/frontend/src/app-components/Datepicker/utils/dateHelpers.test.ts b/libs/form-component/src/app-components/Datepicker/utils/dateHelpers.test.ts similarity index 94% rename from src/App/frontend/src/app-components/Datepicker/utils/dateHelpers.test.ts rename to libs/form-component/src/app-components/Datepicker/utils/dateHelpers.test.ts index 601c826a68a..48846e69772 100644 --- a/src/App/frontend/src/app-components/Datepicker/utils/dateHelpers.test.ts +++ b/libs/form-component/src/app-components/Datepicker/utils/dateHelpers.test.ts @@ -1,4 +1,3 @@ -import { jest } from '@jest/globals'; import { formatISO, isValid, parseISO } from 'date-fns'; import { @@ -10,18 +9,21 @@ import { getDateFormat, getSaveFormattedDateString, strictParseISO, -} from 'src/app-components/Datepicker/utils/dateHelpers'; +} from './dateHelpers'; describe('dateHelpers', () => { beforeAll(() => { /** * Mock todays date to be 2023-07-07T12:54:25.000Z */ - jest.useFakeTimers({ now: 1688734465000 }); + vi.useFakeTimers({ now: 1688734465000 }); }); describe('getDateFormat', () => { - const tests: { props: Parameters; expected: ReturnType }[] = [ + const tests: { + props: Parameters; + expected: ReturnType; + }[] = [ { props: ['YYYY-MM-DD'], expected: 'yyyy-MM-dd' }, { props: ['DD/MM/YYYY'], expected: 'dd/MM/yyyy' }, { props: ['DD.MM.YYYY'], expected: 'dd.MM.yyyy' }, @@ -127,7 +129,10 @@ describe('dateHelpers', () => { }); describe('formatISOString', () => { - const tests: { props: Parameters; expected: ReturnType }[] = [ + const tests: { + props: Parameters; + expected: ReturnType; + }[] = [ { props: [undefined, 'dd/MM/yyyy'], expected: null }, { props: ['2023-13-01', 'dd/MM/yyyy'], expected: null }, { props: ['2023-10-41', 'dd/MM/yyyy'], expected: null }, diff --git a/src/App/frontend/src/app-components/Datepicker/utils/dateHelpers.ts b/libs/form-component/src/app-components/Datepicker/utils/dateHelpers.ts similarity index 93% rename from src/App/frontend/src/app-components/Datepicker/utils/dateHelpers.ts rename to libs/form-component/src/app-components/Datepicker/utils/dateHelpers.ts index fd5efe842ee..4b11d535627 100644 --- a/src/App/frontend/src/app-components/Datepicker/utils/dateHelpers.ts +++ b/libs/form-component/src/app-components/Datepicker/utils/dateHelpers.ts @@ -17,7 +17,7 @@ import { } from 'date-fns'; import type { Locale } from 'date-fns/locale'; -import { locales } from 'src/app-components/Datepicker/utils/dateLocales'; +import { locales } from './dateLocales'; export enum DateFlags { Today = 'today', @@ -53,19 +53,26 @@ export function getDateFormat(format?: string, selectedLanguage = 'nb'): string if (format) { return convertLegacyFormat(format); } - return getLocale(selectedLanguage).formatLong?.date({ width: 'short' }) || DatepickerFormatDefault; + return ( + getLocale(selectedLanguage).formatLong?.date({ width: 'short' }) || DatepickerFormatDefault + ); } export function getSaveFormattedDateString(date: Date | null, timestamp: boolean) { if (date && isValid(date)) { return ( - (!timestamp ? formatISO(date, { representation: 'date' }) : formatISO(date, { representation: 'complete' })) ?? '' + (!timestamp + ? formatISO(date, { representation: 'date' }) + : formatISO(date, { representation: 'complete' })) ?? '' ); } return null; } -export function getDateConstraint(dateOrFlag: string | DateFlags | undefined, constraint: 'min' | 'max'): Date { +export function getDateConstraint( + dateOrFlag: string | DateFlags | undefined, + constraint: 'min' | 'max', +): Date { const shiftTime = constraint === 'min' ? startOfDay : endOfDay; if (dateOrFlag === DateFlags.Today) { @@ -103,7 +110,11 @@ export function getDateConstraint(dateOrFlag: string | DateFlags | undefined, co } } -export function formatISOString(isoString: string | undefined, format: string, locale?: Locale): string | null { +export function formatISOString( + isoString: string | undefined, + format: string, + locale?: Locale, +): string | null { const date = strictParseISO(isoString); if (date && isValid(date)) { @@ -145,7 +156,10 @@ export function strictParseISO(isoString: string | undefined): Date | null { * this function requires that the parsed date when formatted using the same format is equal to the input. * This prevents the value in the Datepicker input from changing while typing. */ -export function strictParseFormat(formattedDate: string | undefined, formatString: string): Date | null { +export function strictParseFormat( + formattedDate: string | undefined, + formatString: string, +): Date | null { if (!formattedDate) { return null; } diff --git a/src/App/frontend/src/app-components/Datepicker/utils/dateLocales.ts b/libs/form-component/src/app-components/Datepicker/utils/dateLocales.ts similarity index 100% rename from src/App/frontend/src/app-components/Datepicker/utils/dateLocales.ts rename to libs/form-component/src/app-components/Datepicker/utils/dateLocales.ts diff --git a/src/App/frontend/src/app-components/Flex/Flex.module.css b/libs/form-component/src/app-components/Flex/Flex.module.css similarity index 100% rename from src/App/frontend/src/app-components/Flex/Flex.module.css rename to libs/form-component/src/app-components/Flex/Flex.module.css diff --git a/src/App/frontend/src/app-components/Flex/Flex.tsx b/libs/form-component/src/app-components/Flex/Flex.tsx similarity index 89% rename from src/App/frontend/src/app-components/Flex/Flex.tsx rename to libs/form-component/src/app-components/Flex/Flex.tsx index fa93e2dcc20..a3d257761d4 100644 --- a/src/App/frontend/src/app-components/Flex/Flex.tsx +++ b/libs/form-component/src/app-components/Flex/Flex.tsx @@ -3,8 +3,17 @@ import type { CSSProperties, ElementType, PropsWithChildren } from 'react'; import cn from 'classnames'; -import classes from 'src/app-components/Flex/Flex.module.css'; -import type { IGridStyling } from 'src/app-components/types'; +import classes from './Flex.module.css'; + +type GridSize = 'auto' | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12; + +export interface IGridStyling { + xs?: GridSize; + sm?: GridSize; + md?: GridSize; + lg?: GridSize; + xl?: GridSize; +} type Spacing = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10; diff --git a/libs/form-component/src/app-components/Flex/index.ts b/libs/form-component/src/app-components/Flex/index.ts new file mode 100644 index 00000000000..ac981d2c6f7 --- /dev/null +++ b/libs/form-component/src/app-components/Flex/index.ts @@ -0,0 +1,2 @@ +export { Flex } from './Flex'; +export type { IGridStyling } from './Flex'; diff --git a/libs/form-component/src/app-components/hooks/index.ts b/libs/form-component/src/app-components/hooks/index.ts new file mode 100644 index 00000000000..7e9fd9d7c26 --- /dev/null +++ b/libs/form-component/src/app-components/hooks/index.ts @@ -0,0 +1,7 @@ +export { + useIsMini, + useIsMobile, + useIsTablet, + useBrowserWidth, + breakpoints, +} from './useDeviceWidths'; diff --git a/src/App/frontend/src/app-components/hooks/useDeviceWidths.ts b/libs/form-component/src/app-components/hooks/useDeviceWidths.ts similarity index 90% rename from src/App/frontend/src/app-components/hooks/useDeviceWidths.ts rename to libs/form-component/src/app-components/hooks/useDeviceWidths.ts index c3e51b65ab2..e441209a123 100644 --- a/src/App/frontend/src/app-components/hooks/useDeviceWidths.ts +++ b/libs/form-component/src/app-components/hooks/useDeviceWidths.ts @@ -10,7 +10,8 @@ type Condition = (width: number) => boolean; const conditionIsMini: Condition = (width) => width <= breakpoints.mini; const conditionIsMobile: Condition = (width) => width <= breakpoints.mobile; -const conditionIsTablet: Condition = (width) => width > breakpoints.mobile && width <= breakpoints.tablet; +const conditionIsTablet: Condition = (width) => + width > breakpoints.mobile && width <= breakpoints.tablet; export function useIsMini() { return useBrowserWidth(conditionIsMini); diff --git a/libs/form-component/src/app-components/index.ts b/libs/form-component/src/app-components/index.ts index ca0b060473a..7ee38fd8bca 100644 --- a/libs/form-component/src/app-components/index.ts +++ b/libs/form-component/src/app-components/index.ts @@ -1 +1,4 @@ export * from './Card'; +export * from './Datepicker'; +export * from './Flex'; +export * from './hooks'; diff --git a/src/App/frontend/monorepo-changed-paths.txt b/src/App/frontend/monorepo-changed-paths.txt index abb25f49cfe..39ef96925c6 100644 --- a/src/App/frontend/monorepo-changed-paths.txt +++ b/src/App/frontend/monorepo-changed-paths.txt @@ -14,7 +14,8 @@ src/__mocks__/getLayoutSetsMock.ts src/__mocks__/getOrgsMock.ts src/app-components/Card/ src/app-components/Date/DisplayDate.tsx -src/app-components/Datepicker/DatePickerHelpers.ts +src/app-components/Datepicker/ +src/app-components/Flex/ src/app-components/Text/DisplayText.tsx src/codegen/schemas/layout-sets.schema.v1.ts src/components/presentation/Header.tsx diff --git a/src/App/frontend/src/app-components/DynamicForm/DynamicForm.tsx b/src/App/frontend/src/app-components/DynamicForm/DynamicForm.tsx index f06da45b795..c67e060f880 100644 --- a/src/App/frontend/src/app-components/DynamicForm/DynamicForm.tsx +++ b/src/App/frontend/src/app-components/DynamicForm/DynamicForm.tsx @@ -1,12 +1,11 @@ import React, { useEffect, useState } from 'react'; import type { MonthCaption } from 'react-day-picker'; +import { DatePickerControl, getDateFormat } from '@app/form-component'; import { Radio, Textfield } from '@digdir/designsystemet-react'; import type { JSONSchema7, JSONSchema7Definition } from 'json-schema'; import { useTranslation } from 'src/app-components/AppComponentsProvider'; -import { DatePickerControl } from 'src/app-components/Datepicker/Datepicker'; -import { getDateFormat } from 'src/app-components/Datepicker/utils/dateHelpers'; import type { TranslationKey } from 'src/app-components/types'; export type FormDataValue = string | number | boolean | null | FormDataValue[] | { [key: string]: FormDataValue }; diff --git a/src/App/frontend/src/app-components/Label/Fieldset.tsx b/src/App/frontend/src/app-components/Label/Fieldset.tsx index 063b51fcd73..1a724ec8718 100644 --- a/src/App/frontend/src/app-components/Label/Fieldset.tsx +++ b/src/App/frontend/src/app-components/Label/Fieldset.tsx @@ -1,11 +1,11 @@ import React from 'react'; import type { JSX, PropsWithChildren, ReactElement } from 'react'; +import { Flex } from '@app/form-component'; import { Fieldset as DesignsystemetFieldset, Label as DesignsystemetLabel } from '@digdir/designsystemet-react'; import cn from 'classnames'; import type { LabelProps as DesignsystemetLabelProps } from '@digdir/designsystemet-react'; -import { Flex } from 'src/app-components/Flex/Flex'; import labelClasses from 'src/app-components/Label/Label.module.css'; import type { IGridStyling } from 'src/app-components/types'; diff --git a/src/App/frontend/src/app-components/Label/Label.tsx b/src/App/frontend/src/app-components/Label/Label.tsx index 3129c47bcab..2875b814893 100644 --- a/src/App/frontend/src/app-components/Label/Label.tsx +++ b/src/App/frontend/src/app-components/Label/Label.tsx @@ -1,11 +1,11 @@ import React, { forwardRef } from 'react'; import type { JSX, PropsWithChildren, ReactElement } from 'react'; +import { Flex } from '@app/form-component'; import { Label as DesignsystemetLabel } from '@digdir/designsystemet-react'; import cn from 'classnames'; import type { LabelProps as DesignsystemetLabelProps } from '@digdir/designsystemet-react'; -import { Flex } from 'src/app-components/Flex/Flex'; import classes from 'src/app-components/Label/Label.module.css'; import type { IGridStyling } from 'src/app-components/types'; diff --git a/src/App/frontend/src/app-components/Pagination/Pagination.tsx b/src/App/frontend/src/app-components/Pagination/Pagination.tsx index bc6f4929862..2dba80c14e6 100644 --- a/src/App/frontend/src/app-components/Pagination/Pagination.tsx +++ b/src/App/frontend/src/app-components/Pagination/Pagination.tsx @@ -1,5 +1,6 @@ import React from 'react'; +import { useIsMini, useIsMobile, useIsTablet } from '@app/form-component'; import { Field, Label, @@ -10,7 +11,6 @@ import { import type { UsePaginationProps } from '@digdir/designsystemet-react'; import { useTranslation } from 'src/app-components/AppComponentsProvider'; -import { useIsMini, useIsMobile, useIsTablet } from 'src/app-components/hooks/useDeviceWidths'; import classes from 'src/app-components/Pagination/Pagination.module.css'; import type { TranslationKey } from 'src/app-components/types'; diff --git a/src/App/frontend/src/app-components/Panel/Panel.tsx b/src/App/frontend/src/app-components/Panel/Panel.tsx index 6f2286e6dd8..edfbd75497c 100644 --- a/src/App/frontend/src/app-components/Panel/Panel.tsx +++ b/src/App/frontend/src/app-components/Panel/Panel.tsx @@ -1,6 +1,7 @@ import React from 'react'; import type { PropsWithChildren } from 'react'; +import { useIsMobile } from '@app/form-component'; import { Heading } from '@digdir/designsystemet-react'; import { CheckmarkCircleIcon, @@ -13,7 +14,6 @@ import cn from 'classnames'; import { useTranslation } from 'src/app-components/AppComponentsProvider'; import { ConditionalWrapper } from 'src/app-components/ConditionalWrapper/ConditionalWrapper'; import { FullWidthWrapper } from 'src/app-components/FullWidthWrapper/FullWidthWrapper'; -import { useIsMobile } from 'src/app-components/hooks/useDeviceWidths'; import { PANEL_VARIANT } from 'src/app-components/Panel/constants'; import classes from 'src/app-components/Panel/Panel.module.css'; import type { TranslationKey } from 'src/app-components/types'; diff --git a/src/App/frontend/src/components/AltinnCollapsible.tsx b/src/App/frontend/src/components/AltinnCollapsible.tsx index fba2951205e..f91631a9b70 100644 --- a/src/App/frontend/src/components/AltinnCollapsible.tsx +++ b/src/App/frontend/src/components/AltinnCollapsible.tsx @@ -1,9 +1,9 @@ import React from 'react'; import type { PropsWithChildren } from 'react'; +import { Flex } from '@app/form-component'; import cn from 'classnames'; -import { Flex } from 'src/app-components/Flex/Flex'; import classes from 'src/components/AltinnCollapsible.module.css'; export interface IAltinnCollapsibleListProps extends PropsWithChildren { diff --git a/src/App/frontend/src/components/altinnParty.tsx b/src/App/frontend/src/components/altinnParty.tsx index 57bd871e91d..bbc519d675d 100644 --- a/src/App/frontend/src/components/altinnParty.tsx +++ b/src/App/frontend/src/components/altinnParty.tsx @@ -1,10 +1,10 @@ import React from 'react'; +import { Flex } from '@app/form-component'; import { Paragraph } from '@digdir/designsystemet-react'; import { Buildings3Icon, ChevronRightCircleFillIcon, PersonIcon } from '@navikt/aksel-icons'; import cn from 'classnames'; -import { Flex } from 'src/app-components/Flex/Flex'; import { AltinnCollapsibleList } from 'src/components/AltinnCollapsible'; import classes from 'src/components/altinnParty.module.css'; import { Lang } from 'src/features/language/Lang'; diff --git a/src/App/frontend/src/components/form/Form.tsx b/src/App/frontend/src/components/form/Form.tsx index 8c27ce3c5b0..760b6a55098 100644 --- a/src/App/frontend/src/components/form/Form.tsx +++ b/src/App/frontend/src/components/form/Form.tsx @@ -1,7 +1,8 @@ import React, { useEffect, useMemo } from 'react'; import { useLocation, useNavigate, useSearchParams } from 'react-router'; -import { Flex } from 'src/app-components/Flex/Flex'; +import { Flex } from '@app/form-component'; + import classes from 'src/components/form/Form.module.css'; import { MessageBanner } from 'src/components/form/MessageBanner'; import { ErrorReport, ErrorReportList } from 'src/components/message/ErrorReport'; diff --git a/src/App/frontend/src/components/label/Label.tsx b/src/App/frontend/src/components/label/Label.tsx index e4a2f862ade..295c846a519 100644 --- a/src/App/frontend/src/components/label/Label.tsx +++ b/src/App/frontend/src/components/label/Label.tsx @@ -1,11 +1,11 @@ import React from 'react'; import type { PropsWithChildren } from 'react'; +import { Flex } from '@app/form-component'; import { Label as DesignsystemetLabel } from '@digdir/designsystemet-react'; import cn from 'classnames'; import type { LabelProps as DesignsystemetLabelProps } from '@digdir/designsystemet-react'; -import { Flex } from 'src/app-components/Flex/Flex'; import classes from 'src/components/label/Label.module.css'; import { LabelContent } from 'src/components/label/LabelContent'; import { useFormComponentCtx } from 'src/layout/FormComponentContext'; diff --git a/src/App/frontend/src/components/message/ErrorReport.tsx b/src/App/frontend/src/components/message/ErrorReport.tsx index 5e8955df2f1..942e9b8e294 100644 --- a/src/App/frontend/src/components/message/ErrorReport.tsx +++ b/src/App/frontend/src/components/message/ErrorReport.tsx @@ -1,9 +1,9 @@ import React, { createContext, useContext } from 'react'; import type { PropsWithChildren } from 'react'; +import { Flex } from '@app/form-component'; import { ErrorSummary } from '@digdir/designsystemet-react'; -import { Flex } from 'src/app-components/Flex/Flex'; import { FullWidthWrapper } from 'src/app-components/FullWidthWrapper/FullWidthWrapper'; import classes from 'src/components/message/ErrorReport.module.css'; import { useAllAttachments } from 'src/features/attachments/hooks'; diff --git a/src/App/frontend/src/components/molecules/AltinnSubstatus.tsx b/src/App/frontend/src/components/molecules/AltinnSubstatus.tsx index b40e6962651..8dcea98f301 100644 --- a/src/App/frontend/src/components/molecules/AltinnSubstatus.tsx +++ b/src/App/frontend/src/components/molecules/AltinnSubstatus.tsx @@ -1,8 +1,8 @@ import React from 'react'; +import { Flex } from '@app/form-component'; import { Paragraph } from '@digdir/designsystemet-react'; -import { Flex } from 'src/app-components/Flex/Flex'; import classes from 'src/components/molecules/AltinnSubstatus.module.css'; export interface IInformationPaperProps { diff --git a/src/App/frontend/src/components/presentation/Header.tsx b/src/App/frontend/src/components/presentation/Header.tsx index a1173a48e52..9ed2bed416d 100644 --- a/src/App/frontend/src/components/presentation/Header.tsx +++ b/src/App/frontend/src/components/presentation/Header.tsx @@ -1,10 +1,10 @@ import React from 'react'; import type { PropsWithChildren } from 'react'; +import { Flex } from '@app/form-component'; import { Heading } from '@digdir/designsystemet-react'; import cn from 'classnames'; -import { Flex } from 'src/app-components/Flex/Flex'; import classes from 'src/components/presentation/Header.module.css'; import { useAppName, useAppOwner } from 'src/core/texts/appTexts'; diff --git a/src/App/frontend/src/components/presentation/Presentation.tsx b/src/App/frontend/src/components/presentation/Presentation.tsx index 2d1e6770111..3ecce91d600 100644 --- a/src/App/frontend/src/components/presentation/Presentation.tsx +++ b/src/App/frontend/src/components/presentation/Presentation.tsx @@ -1,9 +1,9 @@ import React, { useLayoutEffect } from 'react'; import type { PropsWithChildren } from 'react'; +import { Flex } from '@app/form-component'; import cn from 'classnames'; -import { Flex } from 'src/app-components/Flex/Flex'; import { LogoColor } from 'src/components/logo/AltinnLogo'; import { AltinnSubstatus } from 'src/components/molecules/AltinnSubstatus'; import { AppHeader } from 'src/components/presentation/AppHeader/AppHeader'; diff --git a/src/App/frontend/src/components/wrappers/ProcessWrapper.tsx b/src/App/frontend/src/components/wrappers/ProcessWrapper.tsx index 0deeb8ef0f9..ec72f63a6c7 100644 --- a/src/App/frontend/src/components/wrappers/ProcessWrapper.tsx +++ b/src/App/frontend/src/components/wrappers/ProcessWrapper.tsx @@ -1,11 +1,11 @@ import React, { useEffect, useState } from 'react'; import type { PropsWithChildren } from 'react'; +import { Flex } from '@app/form-component'; import { useQueryClient } from '@tanstack/react-query'; import type { QueryClient } from '@tanstack/react-query'; import { Button } from 'src/app-components/Button/Button'; -import { Flex } from 'src/app-components/Flex/Flex'; import { PresentationComponent } from 'src/components/presentation/Presentation'; import classes from 'src/components/wrappers/ProcessWrapper.module.css'; import { Loader } from 'src/core/loading/Loader'; diff --git a/src/App/frontend/src/features/instantiate/containers/InstantiationErrorPage.tsx b/src/App/frontend/src/features/instantiate/containers/InstantiationErrorPage.tsx index db5e8a657ff..be72a3594af 100644 --- a/src/App/frontend/src/features/instantiate/containers/InstantiationErrorPage.tsx +++ b/src/App/frontend/src/features/instantiate/containers/InstantiationErrorPage.tsx @@ -1,6 +1,7 @@ import React from 'react'; -import { Flex } from 'src/app-components/Flex/Flex'; +import { Flex } from '@app/form-component'; + import { AltinnError } from 'src/components/altinnError'; import { InstantiationContainer } from 'src/features/instantiate/containers/InstantiationContainer'; diff --git a/src/App/frontend/src/features/instantiate/containers/PartySelection.tsx b/src/App/frontend/src/features/instantiate/containers/PartySelection.tsx index 426e561d9c5..603db755a9f 100644 --- a/src/App/frontend/src/features/instantiate/containers/PartySelection.tsx +++ b/src/App/frontend/src/features/instantiate/containers/PartySelection.tsx @@ -1,12 +1,12 @@ import React from 'react'; import { useMatch, useNavigate } from 'react-router'; +import { Flex } from '@app/form-component'; import { Checkbox, Heading, Paragraph } from '@digdir/designsystemet-react'; import { PlusIcon } from '@navikt/aksel-icons'; import cn from 'classnames'; import { Button } from 'src/app-components/Button/Button'; -import { Flex } from 'src/app-components/Flex/Flex'; import { Input } from 'src/app-components/Input/Input'; import { translationKey } from 'src/AppComponentsBridge'; import { AltinnParty } from 'src/components/altinnParty'; diff --git a/src/App/frontend/src/features/pdf/PdfFromLayout.tsx b/src/App/frontend/src/features/pdf/PdfFromLayout.tsx index 28282df6250..172b19768da 100644 --- a/src/App/frontend/src/features/pdf/PdfFromLayout.tsx +++ b/src/App/frontend/src/features/pdf/PdfFromLayout.tsx @@ -2,9 +2,9 @@ import React, { useMemo } from 'react'; import { useSearchParams } from 'react-router'; import type { PropsWithChildren } from 'react'; +import { Flex } from '@app/form-component'; import { Heading } from '@digdir/designsystemet-react'; -import { Flex } from 'src/app-components/Flex/Flex'; import { LoadingEmpty } from 'src/app-components/loading/LoadingEmpty/LoadingEmpty'; import { OrganisationLogo } from 'src/components/presentation/OrganisationLogo/OrganisationLogo'; import { DummyPresentation } from 'src/components/presentation/Presentation'; diff --git a/src/App/frontend/src/features/receipt/ReceiptContainer.tsx b/src/App/frontend/src/features/receipt/ReceiptContainer.tsx index 60bfacd26bf..404ce385dbd 100644 --- a/src/App/frontend/src/features/receipt/ReceiptContainer.tsx +++ b/src/App/frontend/src/features/receipt/ReceiptContainer.tsx @@ -1,8 +1,8 @@ import React, { useMemo } from 'react'; +import { PrettyDateAndTime } from '@app/form-component'; import { formatDate } from 'date-fns'; -import { PrettyDateAndTime } from 'src/app-components/Datepicker/utils/dateHelpers'; import { AltinnContentLoader } from 'src/app-components/loading/AltinnContentLoader/AltinnContentLoader'; import { ReceiptComponent } from 'src/components/organisms/AltinnReceipt'; import { ReceiptComponentSimple } from 'src/components/organisms/AltinnReceiptSimple'; diff --git a/src/App/frontend/src/layout/Accordion/Accordion.tsx b/src/App/frontend/src/layout/Accordion/Accordion.tsx index 864ab5ef1c6..0504c182116 100644 --- a/src/App/frontend/src/layout/Accordion/Accordion.tsx +++ b/src/App/frontend/src/layout/Accordion/Accordion.tsx @@ -1,9 +1,9 @@ import React from 'react'; +import { Flex } from '@app/form-component'; import { Card } from '@digdir/designsystemet-react'; import { AccordionItem } from 'src/app-components/Accordion/AccordionItem'; -import { Flex } from 'src/app-components/Flex/Flex'; import { translationKey } from 'src/AppComponentsBridge'; import classes from 'src/layout/Accordion/Accordion.module.css'; import { useIsInAccordionGroup } from 'src/layout/AccordionGroup/AccordionGroupContext'; diff --git a/src/App/frontend/src/layout/Address/AddressComponent.tsx b/src/App/frontend/src/layout/Address/AddressComponent.tsx index eb6d826b0dd..13d8791fdd8 100644 --- a/src/App/frontend/src/layout/Address/AddressComponent.tsx +++ b/src/App/frontend/src/layout/Address/AddressComponent.tsx @@ -1,6 +1,7 @@ import React, { useEffect } from 'react'; -import { Flex } from 'src/app-components/Flex/Flex'; +import { Flex } from '@app/form-component'; + import { Input } from 'src/app-components/Input/Input'; import { Label } from 'src/app-components/Label/Label'; import { HelpTextContainer } from 'src/components/form/HelpTextContainer'; diff --git a/src/App/frontend/src/layout/ButtonGroup/ButtonGroupComponent.tsx b/src/App/frontend/src/layout/ButtonGroup/ButtonGroupComponent.tsx index 4370cee77af..fefe8707ff0 100644 --- a/src/App/frontend/src/layout/ButtonGroup/ButtonGroupComponent.tsx +++ b/src/App/frontend/src/layout/ButtonGroup/ButtonGroupComponent.tsx @@ -1,8 +1,9 @@ import React from 'react'; +import { Flex } from '@app/form-component'; + import type { PropsFromGenericComponent } from '..'; -import { Flex } from 'src/app-components/Flex/Flex'; import { Fieldset } from 'src/app-components/Label/Fieldset'; import classes from 'src/layout/ButtonGroup/ButtonGroupComponent.module.css'; import { ComponentStructureWrapper } from 'src/layout/ComponentStructureWrapper'; diff --git a/src/App/frontend/src/layout/Cards/Cards.tsx b/src/App/frontend/src/layout/Cards/Cards.tsx index b5e01f75363..144cc9788f9 100644 --- a/src/App/frontend/src/layout/Cards/Cards.tsx +++ b/src/App/frontend/src/layout/Cards/Cards.tsx @@ -1,10 +1,9 @@ import React from 'react'; import type { CSSProperties } from 'react'; -import { AppCard } from '@app/form-component'; +import { AppCard, Flex } from '@app/form-component'; import { useTranslation } from 'src/app-components/AppComponentsProvider'; -import { Flex } from 'src/app-components/Flex/Flex'; import { CardProvider } from 'src/layout/Cards/CardContext'; import classes from 'src/layout/Cards/Cards.module.css'; import { ComponentStructureWrapper } from 'src/layout/ComponentStructureWrapper'; diff --git a/src/App/frontend/src/layout/Checkboxes/MultipleChoiceSummary.tsx b/src/App/frontend/src/layout/Checkboxes/MultipleChoiceSummary.tsx index a056fab3df8..ddd58d4a770 100644 --- a/src/App/frontend/src/layout/Checkboxes/MultipleChoiceSummary.tsx +++ b/src/App/frontend/src/layout/Checkboxes/MultipleChoiceSummary.tsx @@ -1,8 +1,8 @@ import React from 'react'; +import { Flex } from '@app/form-component'; import dot from 'dot-object'; -import { Flex } from 'src/app-components/Flex/Flex'; import { Lang } from 'src/features/language/Lang'; import { useLanguage } from 'src/features/language/useLanguage'; import { getCommaSeparatedOptionsToText } from 'src/features/options/getCommaSeparatedOptionsToText'; diff --git a/src/App/frontend/src/layout/ComponentStructureWrapper.tsx b/src/App/frontend/src/layout/ComponentStructureWrapper.tsx index d1cf653e8e6..4358930d716 100644 --- a/src/App/frontend/src/layout/ComponentStructureWrapper.tsx +++ b/src/App/frontend/src/layout/ComponentStructureWrapper.tsx @@ -1,7 +1,8 @@ import React from 'react'; import type { PropsWithChildren } from 'react'; -import { Flex } from 'src/app-components/Flex/Flex'; +import { Flex } from '@app/form-component'; + import { Label } from 'src/components/label/Label'; import { AllComponentValidations } from 'src/features/validation/ComponentValidations'; import { useFormComponentCtx } from 'src/layout/FormComponentContext'; diff --git a/src/App/frontend/src/layout/Datepicker/DatepickerComponent.test.tsx b/src/App/frontend/src/layout/Datepicker/DatepickerComponent.test.tsx index 66f595679df..176d1624112 100644 --- a/src/App/frontend/src/layout/Datepicker/DatepickerComponent.test.tsx +++ b/src/App/frontend/src/layout/Datepicker/DatepickerComponent.test.tsx @@ -10,11 +10,9 @@ import { renderGenericComponentTest } from 'src/test/renderWithProviders'; import type { RenderGenericComponentTestProps } from 'src/test/renderWithProviders'; // Mock dateformat -jest.mock('src/app-components/Datepicker/utils/dateHelpers', () => ({ +jest.mock('@app/form-component', () => ({ __esModules: true, - ...jest.requireActual( - 'src/app-components/Datepicker/utils/dateHelpers', - ), + ...jest.requireActual('@app/form-component'), getDateFormat: jest.fn(() => 'dd.MM.yyyy'), })); diff --git a/src/App/frontend/src/layout/Datepicker/DatepickerComponent.tsx b/src/App/frontend/src/layout/Datepicker/DatepickerComponent.tsx index 2a8cd5be4bf..1ef4fa7f238 100644 --- a/src/App/frontend/src/layout/Datepicker/DatepickerComponent.tsx +++ b/src/App/frontend/src/layout/Datepicker/DatepickerComponent.tsx @@ -1,8 +1,7 @@ import React from 'react'; -import { DatePickerControl } from 'src/app-components/Datepicker/Datepicker'; -import { getDateConstraint, getDateFormat } from 'src/app-components/Datepicker/utils/dateHelpers'; -import { Flex } from 'src/app-components/Flex/Flex'; +import { DatePickerControl, Flex, getDateConstraint, getDateFormat } from '@app/form-component'; + import { Label } from 'src/app-components/Label/Label'; import { useDataModelBindings } from 'src/features/formData/useDataModelBindings'; import { useCurrentLanguage } from 'src/features/language/LanguageProvider'; diff --git a/src/App/frontend/src/layout/Datepicker/DropdownCaption.module.css b/src/App/frontend/src/layout/Datepicker/DropdownCaption.module.css new file mode 100644 index 00000000000..ac25bad06bd --- /dev/null +++ b/src/App/frontend/src/layout/Datepicker/DropdownCaption.module.css @@ -0,0 +1,38 @@ +.datepickerCaption { + display: flex; + margin-bottom: 10px; + margin-left: 10px; + margin-right: 10px; + gap: 4px; +} +.datepickerDropdowns { + display: flex; + gap: 8px; + flex: 1; + min-width: 0; +} + +@media (max-width: 480px) { + .datepickerCaption { + display: grid; + grid-template-columns: 1fr auto; + grid-template-rows: auto auto; + row-gap: 4px; + margin-left: 4px; + margin-right: 4px; + } + .datepickerDropdowns { + grid-column: 1; + grid-row: 1; + } + .datepickerDropdowns > * { + flex: 1; + min-width: 0; + } + .datepickerCloseButton { + display: block; + grid-column: 2; + grid-row: 1; + align-self: center; + } +} diff --git a/src/App/frontend/src/layout/Datepicker/DropdownCaption.tsx b/src/App/frontend/src/layout/Datepicker/DropdownCaption.tsx index c781066efc5..a853a0af4dd 100644 --- a/src/App/frontend/src/layout/Datepicker/DropdownCaption.tsx +++ b/src/App/frontend/src/layout/Datepicker/DropdownCaption.tsx @@ -2,17 +2,16 @@ import React from 'react'; import { formatMonthDropdown, useDayPicker } from 'react-day-picker'; import type { MonthCaptionProps } from 'react-day-picker'; +import { getDateLib, getMonths, getYears, useDatePickerClose } from '@app/form-component'; import { Select } from '@digdir/designsystemet-react'; import { ArrowLeftIcon, ArrowRightIcon, XMarkIcon } from '@navikt/aksel-icons'; import { addYears, setMonth, setYear, startOfMonth, subYears } from 'date-fns'; import { Button } from 'src/app-components/Button/Button'; -import styles from 'src/app-components/Datepicker/Calendar.module.css'; -import { useDatePickerClose } from 'src/app-components/Datepicker/DatepickerDialog'; -import { getDateLib, getMonths, getYears } from 'src/app-components/Datepicker/utils/dateHelpers'; import { translationKey } from 'src/AppComponentsBridge'; import { useCurrentLanguage } from 'src/features/language/LanguageProvider'; import { useLanguage } from 'src/features/language/useLanguage'; +import styles from 'src/layout/Datepicker/DropdownCaption.module.css'; import comboboxClasses from 'src/styles/combobox.module.css'; type DropdownCaptionProps = MonthCaptionProps & { diff --git a/src/App/frontend/src/layout/Datepicker/index.tsx b/src/App/frontend/src/layout/Datepicker/index.tsx index b1b3d832d82..49d5a4507cc 100644 --- a/src/App/frontend/src/layout/Datepicker/index.tsx +++ b/src/App/frontend/src/layout/Datepicker/index.tsx @@ -1,7 +1,8 @@ import React, { forwardRef } from 'react'; import type { JSX } from 'react'; -import { formatISOString, getDateFormat } from 'src/app-components/Datepicker/utils/dateHelpers'; +import { formatISOString, getDateFormat } from '@app/form-component'; + import { useDisplayData } from 'src/features/displayData/useDisplayData'; import { FormStore } from 'src/features/form/FormContext'; import { useCurrentLanguage } from 'src/features/language/LanguageProvider'; diff --git a/src/App/frontend/src/layout/Datepicker/useDatepickerValidation.ts b/src/App/frontend/src/layout/Datepicker/useDatepickerValidation.ts index d7b21399e7b..1626fca75a5 100644 --- a/src/App/frontend/src/layout/Datepicker/useDatepickerValidation.ts +++ b/src/App/frontend/src/layout/Datepicker/useDatepickerValidation.ts @@ -1,6 +1,6 @@ +import { getDateConstraint, getDateFormat, strictParseISO } from '@app/form-component'; import { isAfter, isBefore } from 'date-fns'; -import { getDateConstraint, getDateFormat, strictParseISO } from 'src/app-components/Datepicker/utils/dateHelpers'; import { FormStore } from 'src/features/form/FormContext'; import { useCurrentLanguage } from 'src/features/language/LanguageProvider'; import { type ComponentValidation, FrontendValidationSource, ValidationMask } from 'src/features/validation'; diff --git a/src/App/frontend/src/layout/FileUploadWithTag/EditWindowComponent.tsx b/src/App/frontend/src/layout/FileUploadWithTag/EditWindowComponent.tsx index d52db2d6062..dcfc494028f 100644 --- a/src/App/frontend/src/layout/FileUploadWithTag/EditWindowComponent.tsx +++ b/src/App/frontend/src/layout/FileUploadWithTag/EditWindowComponent.tsx @@ -1,11 +1,11 @@ import React, { useState } from 'react'; +import { Flex } from '@app/form-component'; import { EXPERIMENTAL_Suggestion as Suggestion, ValidationMessage } from '@digdir/designsystemet-react'; import deepEqual from 'fast-deep-equal'; import type { SuggestionItem } from '@digdir/designsystemet-react'; import { Button } from 'src/app-components/Button/Button'; -import { Flex } from 'src/app-components/Flex/Flex'; import { AltinnLoader } from 'src/components/AltinnLoader'; import { isAttachmentUploaded } from 'src/features/attachments'; import { useAttachmentsUpdater } from 'src/features/attachments/hooks'; diff --git a/src/App/frontend/src/layout/GenericComponent.tsx b/src/App/frontend/src/layout/GenericComponent.tsx index 83678d7d4c9..2798ddb4946 100644 --- a/src/App/frontend/src/layout/GenericComponent.tsx +++ b/src/App/frontend/src/layout/GenericComponent.tsx @@ -2,11 +2,11 @@ import React, { useEffect, useMemo, useRef } from 'react'; import { useSearchParams } from 'react-router'; import type { SetURLSearchParams } from 'react-router'; +import { Flex } from '@app/form-component'; import classNames from 'classnames'; import { FatalError } from 'src/app-components/error/FatalError/FatalError'; import { FatalErrorEmpty } from 'src/app-components/error/FatalErrorEmpty/FatalErrorEmpty'; -import { Flex } from 'src/app-components/Flex/Flex'; import { SearchParams } from 'src/core/routing/types'; import { useIsNavigating } from 'src/core/routing/useIsNavigating'; import { useDevToolsStore } from 'src/features/devtools/data/DevToolsStore'; diff --git a/src/App/frontend/src/layout/Group/GroupSummary.tsx b/src/App/frontend/src/layout/Group/GroupSummary.tsx index df8a13fed0a..5af053c7d0d 100644 --- a/src/App/frontend/src/layout/Group/GroupSummary.tsx +++ b/src/App/frontend/src/layout/Group/GroupSummary.tsx @@ -1,11 +1,11 @@ import React from 'react'; +import { Flex } from '@app/form-component'; import { Heading } from '@digdir/designsystemet-react'; import cn from 'classnames'; import type { HeadingProps } from '@digdir/designsystemet-react'; import { ConditionalWrapper } from 'src/app-components/ConditionalWrapper/ConditionalWrapper'; -import { Flex } from 'src/app-components/Flex/Flex'; import { Lang } from 'src/features/language/Lang'; import classes from 'src/layout/Group/GroupSummary.module.css'; import { ComponentSummary, SummaryFlexForContainer } from 'src/layout/Summary2/SummaryComponent2/ComponentSummary'; diff --git a/src/App/frontend/src/layout/Image/ImageComponent.tsx b/src/App/frontend/src/layout/Image/ImageComponent.tsx index cb7e022b58d..1084e20d0db 100644 --- a/src/App/frontend/src/layout/Image/ImageComponent.tsx +++ b/src/App/frontend/src/layout/Image/ImageComponent.tsx @@ -1,6 +1,7 @@ import React from 'react'; -import { Flex } from 'src/app-components/Flex/Flex'; +import { Flex } from '@app/form-component'; + import { HelpTextContainer } from 'src/components/form/HelpTextContainer'; import { Lang } from 'src/features/language/Lang'; import { useCurrentLanguage } from 'src/features/language/LanguageProvider'; diff --git a/src/App/frontend/src/layout/InstanceInformation/InstanceInformationComponent.tsx b/src/App/frontend/src/layout/InstanceInformation/InstanceInformationComponent.tsx index 01dccf09207..b4d566b2192 100644 --- a/src/App/frontend/src/layout/InstanceInformation/InstanceInformationComponent.tsx +++ b/src/App/frontend/src/layout/InstanceInformation/InstanceInformationComponent.tsx @@ -1,10 +1,10 @@ import React from 'react'; +import { PrettyDateAndTime } from '@app/form-component'; import { formatDate, formatISO } from 'date-fns'; import type { PropsFromGenericComponent } from '..'; -import { PrettyDateAndTime } from 'src/app-components/Datepicker/utils/dateHelpers'; import { Fieldset } from 'src/app-components/Label/Fieldset'; import { AltinnSummaryTable } from 'src/components/table/AltinnSummaryTable'; import { useAppReceiver } from 'src/core/texts/appTexts'; diff --git a/src/App/frontend/src/layout/NavigationBar/NavigationBarComponent.tsx b/src/App/frontend/src/layout/NavigationBar/NavigationBarComponent.tsx index 448f73f8023..86ccf7940f6 100644 --- a/src/App/frontend/src/layout/NavigationBar/NavigationBarComponent.tsx +++ b/src/App/frontend/src/layout/NavigationBar/NavigationBarComponent.tsx @@ -1,9 +1,9 @@ import React from 'react'; +import { Flex } from '@app/form-component'; import { CaretDownFillIcon } from '@navikt/aksel-icons'; import cn from 'classnames'; -import { Flex } from 'src/app-components/Flex/Flex'; import { Spinner } from 'src/app-components/loading/Spinner/Spinner'; import { FormStore } from 'src/features/form/FormContext'; import { Lang } from 'src/features/language/Lang'; diff --git a/src/App/frontend/src/layout/RepeatingGroup/Container/RepeatingGroupContainer.tsx b/src/App/frontend/src/layout/RepeatingGroup/Container/RepeatingGroupContainer.tsx index 676d63afdf8..7677daebb59 100644 --- a/src/App/frontend/src/layout/RepeatingGroup/Container/RepeatingGroupContainer.tsx +++ b/src/App/frontend/src/layout/RepeatingGroup/Container/RepeatingGroupContainer.tsx @@ -1,11 +1,11 @@ import React, { forwardRef } from 'react'; import type { JSX } from 'react'; +import { Flex } from '@app/form-component'; import { PlusIcon } from '@navikt/aksel-icons'; import { Button } from 'src/app-components/Button/Button'; import { ConditionalWrapper } from 'src/app-components/ConditionalWrapper/ConditionalWrapper'; -import { Flex } from 'src/app-components/Flex/Flex'; import { FullWidthWrapper } from 'src/app-components/FullWidthWrapper/FullWidthWrapper'; import { Fieldset } from 'src/app-components/Label/Fieldset'; import { FormStore } from 'src/features/form/FormContext'; diff --git a/src/App/frontend/src/layout/RepeatingGroup/EditContainer/RepeatingGroupsEditContainer.tsx b/src/App/frontend/src/layout/RepeatingGroup/EditContainer/RepeatingGroupsEditContainer.tsx index 0153c10eed5..04279dc23f4 100644 --- a/src/App/frontend/src/layout/RepeatingGroup/EditContainer/RepeatingGroupsEditContainer.tsx +++ b/src/App/frontend/src/layout/RepeatingGroup/EditContainer/RepeatingGroupsEditContainer.tsx @@ -1,11 +1,11 @@ import React from 'react'; import type { JSX } from 'react'; +import { Flex } from '@app/form-component'; import { ChevronLeftIcon, ChevronRightIcon, TrashIcon } from '@navikt/aksel-icons'; import cn from 'classnames'; import { Button } from 'src/app-components/Button/Button'; -import { Flex } from 'src/app-components/Flex/Flex'; import { FormStore } from 'src/features/form/FormContext'; import { Lang } from 'src/features/language/Lang'; import { GenericComponent } from 'src/layout/GenericComponent'; diff --git a/src/App/frontend/src/layout/RepeatingGroup/Summary2/RepeatingGroupSummary.tsx b/src/App/frontend/src/layout/RepeatingGroup/Summary2/RepeatingGroupSummary.tsx index 6c88ff257f2..46cf62fb5b2 100644 --- a/src/App/frontend/src/layout/RepeatingGroup/Summary2/RepeatingGroupSummary.tsx +++ b/src/App/frontend/src/layout/RepeatingGroup/Summary2/RepeatingGroupSummary.tsx @@ -1,9 +1,9 @@ import React from 'react'; +import { Flex } from '@app/form-component'; import { Heading, ValidationMessage } from '@digdir/designsystemet-react'; import cn from 'classnames'; -import { Flex } from 'src/app-components/Flex/Flex'; import { FormStore } from 'src/features/form/FormContext'; import { Lang } from 'src/features/language/Lang'; import { useUnifiedValidationsForNode } from 'src/features/validation/selectors/unifiedValidationsForNode'; diff --git a/src/App/frontend/src/layout/RepeatingGroup/Table/RepeatingGroupTableRow.tsx b/src/App/frontend/src/layout/RepeatingGroup/Table/RepeatingGroupTableRow.tsx index c2bce09c276..c7c70d44f8b 100644 --- a/src/App/frontend/src/layout/RepeatingGroup/Table/RepeatingGroupTableRow.tsx +++ b/src/App/frontend/src/layout/RepeatingGroup/Table/RepeatingGroupTableRow.tsx @@ -1,12 +1,12 @@ import React, { useLayoutEffect } from 'react'; import type { JSX } from 'react'; +import { Flex } from '@app/form-component'; import { Table } from '@digdir/designsystemet-react'; import { PencilIcon, TrashIcon, XMarkOctagonFillIcon } from '@navikt/aksel-icons'; import cn from 'classnames'; import { Button } from 'src/app-components/Button/Button'; -import { Flex } from 'src/app-components/Flex/Flex'; import { translationKey } from 'src/AppComponentsBridge'; import { DeleteWarningPopover } from 'src/features/alertOnChange/DeleteWarningPopover'; import { useAlertOnChange } from 'src/features/alertOnChange/useAlertOnChange'; diff --git a/src/App/frontend/src/layout/Subform/SubformComponent.tsx b/src/App/frontend/src/layout/Subform/SubformComponent.tsx index f034c350e24..591fdd3fb50 100644 --- a/src/App/frontend/src/layout/Subform/SubformComponent.tsx +++ b/src/App/frontend/src/layout/Subform/SubformComponent.tsx @@ -1,13 +1,13 @@ import React from 'react'; import { useNavigation } from 'react-router'; +import { Flex } from '@app/form-component'; import { Table } from '@digdir/designsystemet-react'; import { PencilIcon, PlusIcon, TrashIcon } from '@navikt/aksel-icons'; import cn from 'classnames'; import { Button } from 'src/app-components/Button/Button'; import { FatalError } from 'src/app-components/error/FatalError/FatalError'; -import { Flex } from 'src/app-components/Flex/Flex'; import { Spinner } from 'src/app-components/loading/Spinner/Spinner'; import { translationKey } from 'src/AppComponentsBridge'; import { Caption } from 'src/components/form/caption/Caption'; diff --git a/src/App/frontend/src/layout/Subform/Summary/SubformSummaryComponent2.tsx b/src/App/frontend/src/layout/Subform/Summary/SubformSummaryComponent2.tsx index a62ed724cf0..70ba7339f8b 100644 --- a/src/App/frontend/src/layout/Subform/Summary/SubformSummaryComponent2.tsx +++ b/src/App/frontend/src/layout/Subform/Summary/SubformSummaryComponent2.tsx @@ -1,8 +1,8 @@ import React, { Fragment, type PropsWithChildren } from 'react'; +import { Flex } from '@app/form-component'; import { Heading, Paragraph } from '@digdir/designsystemet-react'; -import { Flex } from 'src/app-components/Flex/Flex'; import { Label, LabelInner } from 'src/components/label/Label'; import { TaskOverrides } from 'src/core/contexts/TaskOverrides'; import { DisplayError } from 'src/core/errorHandling/DisplayError'; diff --git a/src/App/frontend/src/layout/Subform/Summary/SubformSummaryTable.tsx b/src/App/frontend/src/layout/Subform/Summary/SubformSummaryTable.tsx index 5dc77cfd967..77bab2d3a3e 100644 --- a/src/App/frontend/src/layout/Subform/Summary/SubformSummaryTable.tsx +++ b/src/App/frontend/src/layout/Subform/Summary/SubformSummaryTable.tsx @@ -1,11 +1,11 @@ import React from 'react'; import { useNavigate, useNavigation } from 'react-router'; +import { Flex } from '@app/form-component'; import { Paragraph, Table } from '@digdir/designsystemet-react'; import classNames from 'classnames'; import { FatalError } from 'src/app-components/error/FatalError/FatalError'; -import { Flex } from 'src/app-components/Flex/Flex'; import { Spinner } from 'src/app-components/loading/Spinner/Spinner'; import { Caption } from 'src/components/form/caption/Caption'; import { Label } from 'src/components/label/Label'; diff --git a/src/App/frontend/src/layout/Summary/SummaryComponent.tsx b/src/App/frontend/src/layout/Summary/SummaryComponent.tsx index 74a939c6475..66129e7ff56 100644 --- a/src/App/frontend/src/layout/Summary/SummaryComponent.tsx +++ b/src/App/frontend/src/layout/Summary/SummaryComponent.tsx @@ -1,8 +1,8 @@ import React from 'react'; +import { Flex } from '@app/form-component'; import cn from 'classnames'; -import { Flex } from 'src/app-components/Flex/Flex'; import { ErrorPaper } from 'src/components/message/ErrorPaper'; import { FormStore } from 'src/features/form/FormContext'; import { Lang } from 'src/features/language/Lang'; diff --git a/src/App/frontend/src/layout/Summary2/CommonSummaryComponents/LayoutSetSummaryAccordion.tsx b/src/App/frontend/src/layout/Summary2/CommonSummaryComponents/LayoutSetSummaryAccordion.tsx index a3ceef85bea..b52286440e4 100644 --- a/src/App/frontend/src/layout/Summary2/CommonSummaryComponents/LayoutSetSummaryAccordion.tsx +++ b/src/App/frontend/src/layout/Summary2/CommonSummaryComponents/LayoutSetSummaryAccordion.tsx @@ -1,7 +1,8 @@ import React from 'react'; +import { Flex } from '@app/form-component'; + import { AccordionItem } from 'src/app-components/Accordion/AccordionItem'; -import { Flex } from 'src/app-components/Flex/Flex'; import { translationKey } from 'src/AppComponentsBridge'; import classes from 'src/layout/Summary2/CommonSummaryComponents/LayoutSetSummaryAccordion.module.css'; import { EmptyChildrenBoundary } from 'src/layout/Summary2/isEmpty/EmptyChildrenContext'; diff --git a/src/App/frontend/src/layout/Summary2/SummaryComponent2/ComponentSummary.tsx b/src/App/frontend/src/layout/Summary2/SummaryComponent2/ComponentSummary.tsx index 80f21b7180b..009f4cc8ee8 100644 --- a/src/App/frontend/src/layout/Summary2/SummaryComponent2/ComponentSummary.tsx +++ b/src/App/frontend/src/layout/Summary2/SummaryComponent2/ComponentSummary.tsx @@ -1,9 +1,9 @@ import React from 'react'; import type { JSX, PropsWithChildren } from 'react'; +import { Flex } from '@app/form-component'; import cn from 'classnames'; -import { Flex } from 'src/app-components/Flex/Flex'; import { useDevToolsStore } from 'src/features/devtools/data/DevToolsStore'; import { getComponentDef } from 'src/layout'; import { CompCategory } from 'src/layout/common'; diff --git a/src/App/frontend/src/layout/Summary2/SummaryComponent2/PageSummary.tsx b/src/App/frontend/src/layout/Summary2/SummaryComponent2/PageSummary.tsx index df7146f73ac..14a9b6d762a 100644 --- a/src/App/frontend/src/layout/Summary2/SummaryComponent2/PageSummary.tsx +++ b/src/App/frontend/src/layout/Summary2/SummaryComponent2/PageSummary.tsx @@ -1,7 +1,8 @@ import React from 'react'; import type { CSSProperties } from 'react'; -import { Flex } from 'src/app-components/Flex/Flex'; +import { Flex } from '@app/form-component'; + import { FormStore } from 'src/features/form/FormContext'; import { ComponentSummary, HideWhenAllChildrenEmpty } from 'src/layout/Summary2/SummaryComponent2/ComponentSummary'; import { useSummaryOverridesForPage, useSummaryProp } from 'src/layout/Summary2/summaryStoreContext'; diff --git a/src/App/frontend/src/layout/Tabs/Tabs.tsx b/src/App/frontend/src/layout/Tabs/Tabs.tsx index 36df9f30271..a032f434d67 100644 --- a/src/App/frontend/src/layout/Tabs/Tabs.tsx +++ b/src/App/frontend/src/layout/Tabs/Tabs.tsx @@ -1,9 +1,9 @@ import React, { useEffect, useState } from 'react'; import { useSearchParams } from 'react-router'; +import { Flex } from '@app/form-component'; import { Tabs as DesignsystemetTabs } from '@digdir/designsystemet-react'; -import { Flex } from 'src/app-components/Flex/Flex'; import { SearchParams } from 'src/core/routing/types'; import { FormStore } from 'src/features/form/FormContext'; import { Lang } from 'src/features/language/Lang'; diff --git a/src/App/frontend/src/layout/Tabs/TabsSummary.tsx b/src/App/frontend/src/layout/Tabs/TabsSummary.tsx index bb2d7ecb154..48ea5ac91cb 100644 --- a/src/App/frontend/src/layout/Tabs/TabsSummary.tsx +++ b/src/App/frontend/src/layout/Tabs/TabsSummary.tsx @@ -1,8 +1,8 @@ import React from 'react'; +import { Flex } from '@app/form-component'; import { Heading } from '@digdir/designsystemet-react'; -import { Flex } from 'src/app-components/Flex/Flex'; import { Lang } from 'src/features/language/Lang'; import { ComponentSummary, SummaryFlexForContainer } from 'src/layout/Summary2/SummaryComponent2/ComponentSummary'; import { useSummaryProp } from 'src/layout/Summary2/summaryStoreContext'; diff --git a/src/App/frontend/src/layout/TimePicker/TimePickerComponent.tsx b/src/App/frontend/src/layout/TimePicker/TimePickerComponent.tsx index 94f8adf6967..ebdd646a2ff 100644 --- a/src/App/frontend/src/layout/TimePicker/TimePickerComponent.tsx +++ b/src/App/frontend/src/layout/TimePicker/TimePickerComponent.tsx @@ -1,6 +1,7 @@ import React from 'react'; -import { Flex } from 'src/app-components/Flex/Flex'; +import { Flex } from '@app/form-component'; + import { Label } from 'src/app-components/Label/Label'; import { TimePicker as TimePickerControl } from 'src/app-components/TimePicker/TimePicker'; import { translationKey } from 'src/AppComponentsBridge'; diff --git a/src/App/frontend/src/utils/dateUtils.test.ts b/src/App/frontend/src/utils/dateUtils.test.ts index 0ec69823bbf..be59f69924b 100644 --- a/src/App/frontend/src/utils/dateUtils.test.ts +++ b/src/App/frontend/src/utils/dateUtils.test.ts @@ -1,4 +1,5 @@ -import { PrettyDateAndTime } from 'src/app-components/Datepicker/utils/dateHelpers'; +import { PrettyDateAndTime } from '@app/form-component'; + import { formatDateLocale } from 'src/utils/dateUtils'; import type { Token } from 'src/utils/dateUtils'; From 6aa43c2bb5596357749e5c2e1acb8071fc81274c Mon Sep 17 00:00:00 2001 From: Adam Haeger Date: Fri, 15 May 2026 15:08:26 +0200 Subject: [PATCH 02/13] fix --- src/App/frontend/src/layout/Datepicker/DropdownCaption.tsx | 2 +- .../CommonSummaryComponents/LayoutSetSummaryAccordion.tsx | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/App/frontend/src/layout/Datepicker/DropdownCaption.tsx b/src/App/frontend/src/layout/Datepicker/DropdownCaption.tsx index 9a1bb5498ef..8bf3724fedc 100644 --- a/src/App/frontend/src/layout/Datepicker/DropdownCaption.tsx +++ b/src/App/frontend/src/layout/Datepicker/DropdownCaption.tsx @@ -7,9 +7,9 @@ import { Select } from '@digdir/designsystemet-react'; import { ArrowLeftIcon, ArrowRightIcon, XMarkIcon } from '@navikt/aksel-icons'; import { addYears, setMonth, setYear, startOfMonth, subYears } from 'date-fns'; -import styles from 'src/app-components/Datepicker/Calendar.module.css'; import { useCurrentLanguage } from 'src/features/language/LanguageProvider'; import { useLanguage } from 'src/features/language/useLanguage'; +import styles from 'src/layout/Datepicker/DropdownCaption.module.css'; import comboboxClasses from 'src/styles/combobox.module.css'; type DropdownCaptionProps = MonthCaptionProps & { diff --git a/src/App/frontend/src/layout/Summary2/CommonSummaryComponents/LayoutSetSummaryAccordion.tsx b/src/App/frontend/src/layout/Summary2/CommonSummaryComponents/LayoutSetSummaryAccordion.tsx index 95a665da9d9..504dbd233a5 100644 --- a/src/App/frontend/src/layout/Summary2/CommonSummaryComponents/LayoutSetSummaryAccordion.tsx +++ b/src/App/frontend/src/layout/Summary2/CommonSummaryComponents/LayoutSetSummaryAccordion.tsx @@ -1,7 +1,6 @@ import React from 'react'; -import { AccordionItem } from '@app/form-component/src/app-components/AccordionItem/AccordionItem'; -import { Flex } from '@app/form-component/src/app-components/Flex/Flex'; +import { AccordionItem, Flex } from '@app/form-component'; import { useTranslation } from 'src/app-components/AppComponentsProvider'; import classes from 'src/layout/Summary2/CommonSummaryComponents/LayoutSetSummaryAccordion.module.css'; From ba1ac3c1e65ddcf717eb3e7e0a377e113272c2a5 Mon Sep 17 00:00:00 2001 From: Adam Haeger Date: Fri, 15 May 2026 15:13:13 +0200 Subject: [PATCH 03/13] changed paths update --- src/App/frontend/monorepo-changed-paths.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/App/frontend/monorepo-changed-paths.txt b/src/App/frontend/monorepo-changed-paths.txt index 7d361418790..7edd894c8d9 100644 --- a/src/App/frontend/monorepo-changed-paths.txt +++ b/src/App/frontend/monorepo-changed-paths.txt @@ -2,7 +2,7 @@ .github/ .husky/ .yarn/patches/jsdom-npm-26.1.0-3857255f02.patch -.yarn/releases/yarn-4.12.0.cjs +.yarn/releases/yarn-4.14.1.cjs .yarnrc.yml LICENSE.md adr/001-component-library.md From 4cdd85b4a44da9efa451681b6d2cb6808c487dcf Mon Sep 17 00:00:00 2001 From: Adam Haeger Date: Mon, 18 May 2026 08:53:22 +0200 Subject: [PATCH 04/13] extracted mocks to __mocks__ --- src/App/frontend/src/__mocks__/@app/form-component.ts | 7 +++++++ .../src/layout/Datepicker/DatepickerComponent.test.tsx | 8 ++------ 2 files changed, 9 insertions(+), 6 deletions(-) create mode 100644 src/App/frontend/src/__mocks__/@app/form-component.ts diff --git a/src/App/frontend/src/__mocks__/@app/form-component.ts b/src/App/frontend/src/__mocks__/@app/form-component.ts new file mode 100644 index 00000000000..298f427ce72 --- /dev/null +++ b/src/App/frontend/src/__mocks__/@app/form-component.ts @@ -0,0 +1,7 @@ +const actual = jest.requireActual('@app/form-component'); + +module.exports = { + __esModule: true, + ...actual, + getDateFormat: jest.fn(() => 'dd.MM.yyyy'), +}; diff --git a/src/App/frontend/src/layout/Datepicker/DatepickerComponent.test.tsx b/src/App/frontend/src/layout/Datepicker/DatepickerComponent.test.tsx index 176d1624112..3453f67967e 100644 --- a/src/App/frontend/src/layout/Datepicker/DatepickerComponent.test.tsx +++ b/src/App/frontend/src/layout/Datepicker/DatepickerComponent.test.tsx @@ -9,12 +9,8 @@ import { mockMediaQuery } from 'src/test/mockMediaQuery'; import { renderGenericComponentTest } from 'src/test/renderWithProviders'; import type { RenderGenericComponentTestProps } from 'src/test/renderWithProviders'; -// Mock dateformat -jest.mock('@app/form-component', () => ({ - __esModules: true, - ...jest.requireActual('@app/form-component'), - getDateFormat: jest.fn(() => 'dd.MM.yyyy'), -})); +// Mock dateformat (see src/__mocks__/@app/form-component.ts) +jest.mock('@app/form-component'); const render = async ({ component, ...rest }: Partial> = {}) => await renderGenericComponentTest({ From 731f2a234ee4f619d965ab7ed7902cf297d65dc0 Mon Sep 17 00:00:00 2001 From: Adam Haeger Date: Mon, 18 May 2026 13:18:31 +0200 Subject: [PATCH 05/13] importing from @app --- .../src/app-components/Datepicker/DatepickerDialog.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libs/form-component/src/app-components/Datepicker/DatepickerDialog.tsx b/libs/form-component/src/app-components/Datepicker/DatepickerDialog.tsx index 2f56736e130..4689242c9be 100644 --- a/libs/form-component/src/app-components/Datepicker/DatepickerDialog.tsx +++ b/libs/form-component/src/app-components/Datepicker/DatepickerDialog.tsx @@ -2,9 +2,8 @@ import { createContext, useContext, useEffect, useRef } from 'react'; import type { PropsWithChildren, ReactNode } from 'react'; import { Dialog, Popover } from '@digdir/designsystemet-react'; - -import { useIsMobile } from '../hooks/useDeviceWidths'; import styles from './Calendar.module.css'; +import { useIsMobile } from '@app/form-component'; const DatePickerCloseContext = createContext<(() => void) | null>(null); From 3124efd7ce6ee9a64f5ca2f13d71b166521b67fc Mon Sep 17 00:00:00 2001 From: Adam Haeger Date: Mon, 18 May 2026 13:40:10 +0200 Subject: [PATCH 06/13] cleanup after code review --- libs/form-component/package.json | 5 ----- .../src/app-components/Button/Button.tsx | 3 +-- .../src/app-components/Datepicker/Datepicker.tsx | 2 +- src/App/frontend/src/__mocks__/@app/form-component.ts | 7 ------- .../src/layout/Datepicker/DatepickerComponent.test.tsx | 8 ++++++-- yarn.lock | 10 +++++----- 6 files changed, 13 insertions(+), 22 deletions(-) delete mode 100644 src/App/frontend/src/__mocks__/@app/form-component.ts diff --git a/libs/form-component/package.json b/libs/form-component/package.json index a290d0ebc42..c68dc8da2b8 100644 --- a/libs/form-component/package.json +++ b/libs/form-component/package.json @@ -11,7 +11,6 @@ "scripts": { "test": "vitest", "test:ci": "vitest run", - "lint": "eslint \"src/**/*.ts*\"", "typecheck": "tsc --noEmit -p ./", "storybook": "storybook dev -p 6008", "build-storybook": "storybook build" @@ -43,11 +42,7 @@ "@types/react": "19.2.14", "@types/react-dom": "19.2.3", "@vitejs/plugin-react": "^5.0.0", - "classnames": "2.5.1", - "date-fns": "4.1.0", "jsdom": "^26.1.0", - "react-day-picker": "9.14.0", - "react-number-format": "5.4.5", "storybook": "^10.2.11", "typescript": "5.9.3", "typescript-plugin-css-modules": "5.2.0", diff --git a/libs/form-component/src/app-components/Button/Button.tsx b/libs/form-component/src/app-components/Button/Button.tsx index 23894acf603..71f7255baa1 100644 --- a/libs/form-component/src/app-components/Button/Button.tsx +++ b/libs/form-component/src/app-components/Button/Button.tsx @@ -3,8 +3,7 @@ import type { PropsWithChildren } from 'react'; import { Button as DesignSystemButton } from '@digdir/designsystemet-react'; import type { ButtonProps as DesignSystemButtonProps } from '@digdir/designsystemet-react'; - -import { Spinner } from '../Spinner'; +import { Spinner } from '@app/form-component'; export type ButtonVariant = 'primary' | 'secondary' | 'tertiary' | undefined; export type ButtonColor = 'first' | 'second' | 'success' | 'danger' | undefined; diff --git a/libs/form-component/src/app-components/Datepicker/Datepicker.tsx b/libs/form-component/src/app-components/Datepicker/Datepicker.tsx index 1118d431779..b19032f1f15 100644 --- a/libs/form-component/src/app-components/Datepicker/Datepicker.tsx +++ b/libs/form-component/src/app-components/Datepicker/Datepicker.tsx @@ -4,7 +4,7 @@ import type { MonthCaption } from 'react-day-picker'; import { CalendarIcon } from '@navikt/aksel-icons'; import { isValid as isValidDate } from 'date-fns'; -import { Flex } from '../Flex/Flex'; +import { Flex } from '@app/form-component'; import styles from './Calendar.module.css'; import { DatePickerCalendar } from './DatePickerCalendar'; import { DatePickerDialog } from './DatepickerDialog'; diff --git a/src/App/frontend/src/__mocks__/@app/form-component.ts b/src/App/frontend/src/__mocks__/@app/form-component.ts deleted file mode 100644 index 298f427ce72..00000000000 --- a/src/App/frontend/src/__mocks__/@app/form-component.ts +++ /dev/null @@ -1,7 +0,0 @@ -const actual = jest.requireActual('@app/form-component'); - -module.exports = { - __esModule: true, - ...actual, - getDateFormat: jest.fn(() => 'dd.MM.yyyy'), -}; diff --git a/src/App/frontend/src/layout/Datepicker/DatepickerComponent.test.tsx b/src/App/frontend/src/layout/Datepicker/DatepickerComponent.test.tsx index 3453f67967e..176d1624112 100644 --- a/src/App/frontend/src/layout/Datepicker/DatepickerComponent.test.tsx +++ b/src/App/frontend/src/layout/Datepicker/DatepickerComponent.test.tsx @@ -9,8 +9,12 @@ import { mockMediaQuery } from 'src/test/mockMediaQuery'; import { renderGenericComponentTest } from 'src/test/renderWithProviders'; import type { RenderGenericComponentTestProps } from 'src/test/renderWithProviders'; -// Mock dateformat (see src/__mocks__/@app/form-component.ts) -jest.mock('@app/form-component'); +// Mock dateformat +jest.mock('@app/form-component', () => ({ + __esModules: true, + ...jest.requireActual('@app/form-component'), + getDateFormat: jest.fn(() => 'dd.MM.yyyy'), +})); const render = async ({ component, ...rest }: Partial> = {}) => await renderGenericComponentTest({ diff --git a/yarn.lock b/yarn.lock index deedec42223..96e921ff551 100644 --- a/yarn.lock +++ b/yarn.lock @@ -143,10 +143,10 @@ __metadata: "@types/react-dom": "npm:19.2.3" "@vitejs/plugin-react": "npm:^5.0.0" classnames: "npm:2.5.1" - date-fns: "npm:4.1.0" + date-fns: "npm:^4.1.0" jsdom: "npm:^26.1.0" - react-day-picker: "npm:9.14.0" - react-number-format: "npm:5.4.5" + react-day-picker: "npm:^9.14.0" + react-number-format: "npm:^5.4.5" storybook: "npm:^10.2.11" typescript: "npm:5.9.3" typescript-plugin-css-modules: "npm:5.2.0" @@ -20509,7 +20509,7 @@ __metadata: languageName: node linkType: hard -"react-day-picker@npm:9.14.0": +"react-day-picker@npm:9.14.0, react-day-picker@npm:^9.14.0": version: 9.14.0 resolution: "react-day-picker@npm:9.14.0" dependencies: @@ -20700,7 +20700,7 @@ __metadata: languageName: node linkType: hard -"react-number-format@npm:5.4.5": +"react-number-format@npm:5.4.5, react-number-format@npm:^5.4.5": version: 5.4.5 resolution: "react-number-format@npm:5.4.5" peerDependencies: From d4e361dce3e511fbfdaffcdc3d1ebbb7fdf015ad Mon Sep 17 00:00:00 2001 From: Adam Haeger Date: Mon, 18 May 2026 14:36:47 +0200 Subject: [PATCH 07/13] fixed broken imports --- .../src/app-components/Button/Button.tsx | 3 +- .../Datepicker/DatePickerCalendar.tsx | 1 + .../app-components/Datepicker/Datepicker.tsx | 2 +- .../Datepicker/DatepickerDialog.tsx | 5 +- src/App/frontend/package.json | 1 + .../Datepicker/DatepickerComponent.test.tsx | 2 +- yarn.lock | 65 +++++++++++++++++++ 7 files changed, 74 insertions(+), 5 deletions(-) diff --git a/libs/form-component/src/app-components/Button/Button.tsx b/libs/form-component/src/app-components/Button/Button.tsx index 71f7255baa1..23894acf603 100644 --- a/libs/form-component/src/app-components/Button/Button.tsx +++ b/libs/form-component/src/app-components/Button/Button.tsx @@ -3,7 +3,8 @@ import type { PropsWithChildren } from 'react'; import { Button as DesignSystemButton } from '@digdir/designsystemet-react'; import type { ButtonProps as DesignSystemButtonProps } from '@digdir/designsystemet-react'; -import { Spinner } from '@app/form-component'; + +import { Spinner } from '../Spinner'; export type ButtonVariant = 'primary' | 'secondary' | 'tertiary' | undefined; export type ButtonColor = 'first' | 'second' | 'success' | 'danger' | undefined; diff --git a/libs/form-component/src/app-components/Datepicker/DatePickerCalendar.tsx b/libs/form-component/src/app-components/Datepicker/DatePickerCalendar.tsx index 93e3147e85f..803cc1af8fc 100644 --- a/libs/form-component/src/app-components/Datepicker/DatePickerCalendar.tsx +++ b/libs/form-component/src/app-components/Datepicker/DatePickerCalendar.tsx @@ -1,3 +1,4 @@ +import React from 'react'; import { DayPicker } from 'react-day-picker'; import type { Matcher, MonthCaption } from 'react-day-picker'; diff --git a/libs/form-component/src/app-components/Datepicker/Datepicker.tsx b/libs/form-component/src/app-components/Datepicker/Datepicker.tsx index b19032f1f15..1118d431779 100644 --- a/libs/form-component/src/app-components/Datepicker/Datepicker.tsx +++ b/libs/form-component/src/app-components/Datepicker/Datepicker.tsx @@ -4,7 +4,7 @@ import type { MonthCaption } from 'react-day-picker'; import { CalendarIcon } from '@navikt/aksel-icons'; import { isValid as isValidDate } from 'date-fns'; -import { Flex } from '@app/form-component'; +import { Flex } from '../Flex/Flex'; import styles from './Calendar.module.css'; import { DatePickerCalendar } from './DatePickerCalendar'; import { DatePickerDialog } from './DatepickerDialog'; diff --git a/libs/form-component/src/app-components/Datepicker/DatepickerDialog.tsx b/libs/form-component/src/app-components/Datepicker/DatepickerDialog.tsx index 4689242c9be..b14cc447868 100644 --- a/libs/form-component/src/app-components/Datepicker/DatepickerDialog.tsx +++ b/libs/form-component/src/app-components/Datepicker/DatepickerDialog.tsx @@ -1,9 +1,10 @@ -import { createContext, useContext, useEffect, useRef } from 'react'; +import React, { createContext, useContext, useEffect, useRef } from 'react'; import type { PropsWithChildren, ReactNode } from 'react'; import { Dialog, Popover } from '@digdir/designsystemet-react'; + +import { useIsMobile } from '../hooks/useDeviceWidths'; import styles from './Calendar.module.css'; -import { useIsMobile } from '@app/form-component'; const DatePickerCloseContext = createContext<(() => void) | null>(null); diff --git a/src/App/frontend/package.json b/src/App/frontend/package.json index 6bca0c026c3..58ce96711f0 100644 --- a/src/App/frontend/package.json +++ b/src/App/frontend/package.json @@ -108,6 +108,7 @@ "jest-junit": "16.0.0", "jest-mock-axios": "4.9.0", "jest-preview": "0.3.3", + "jest-silent-reporter": "^0.6.0", "js-levenshtein": "1.1.6", "jsdom": "patch:jsdom@npm%3A26.1.0#~/.yarn/patches/jsdom-npm-26.1.0-3857255f02.patch", "mime": "4.1.0", diff --git a/src/App/frontend/src/layout/Datepicker/DatepickerComponent.test.tsx b/src/App/frontend/src/layout/Datepicker/DatepickerComponent.test.tsx index 176d1624112..92ff6e7a606 100644 --- a/src/App/frontend/src/layout/Datepicker/DatepickerComponent.test.tsx +++ b/src/App/frontend/src/layout/Datepicker/DatepickerComponent.test.tsx @@ -11,7 +11,7 @@ import type { RenderGenericComponentTestProps } from 'src/test/renderWithProvide // Mock dateformat jest.mock('@app/form-component', () => ({ - __esModules: true, + __esModule: true, ...jest.requireActual('@app/form-component'), getDateFormat: jest.fn(() => 'dd.MM.yyyy'), })); diff --git a/yarn.lock b/yarn.lock index 96e921ff551..b008625e89d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3882,6 +3882,19 @@ __metadata: languageName: node linkType: hard +"@jest/types@npm:^26.6.2": + version: 26.6.2 + resolution: "@jest/types@npm:26.6.2" + dependencies: + "@types/istanbul-lib-coverage": "npm:^2.0.0" + "@types/istanbul-reports": "npm:^3.0.0" + "@types/node": "npm:*" + "@types/yargs": "npm:^15.0.0" + chalk: "npm:^4.0.0" + checksum: 10/02d42749c8c6dc7e3184d0ff0293dd91c97233c2e6dc3708d61ef33d3162d4f07ad38d2d8a39abd94cf2fced69b92a87565c7099137c4529809242ca327254af + languageName: node + linkType: hard + "@jest/types@npm:^29.6.3": version: 29.6.3 resolution: "@jest/types@npm:29.6.3" @@ -7675,6 +7688,15 @@ __metadata: languageName: node linkType: hard +"@types/yargs@npm:^15.0.0": + version: 15.0.20 + resolution: "@types/yargs@npm:15.0.20" + dependencies: + "@types/yargs-parser": "npm:*" + checksum: 10/f348069c4a0cf5b365e72507f67c6569b12a4af44346c08288319d522272dbe1e3f3acde3ff9ab72bd9f894676624d10fff21096d44bad33e390d092cd409aeb + languageName: node + linkType: hard + "@types/yargs@npm:^17.0.33, @types/yargs@npm:^17.0.8": version: 17.0.35 resolution: "@types/yargs@npm:17.0.35" @@ -9103,6 +9125,7 @@ __metadata: jest-junit: "npm:16.0.0" jest-mock-axios: "npm:4.9.0" jest-preview: "npm:0.3.3" + jest-silent-reporter: "npm:^0.6.0" js-levenshtein: "npm:1.1.6" jsdom: "patch:jsdom@npm%3A26.1.0#~/.yarn/patches/jsdom-npm-26.1.0-3857255f02.patch" jsonpointer: "npm:5.0.1" @@ -10505,6 +10528,13 @@ __metadata: languageName: node linkType: hard +"ci-info@npm:^2.0.0": + version: 2.0.0 + resolution: "ci-info@npm:2.0.0" + checksum: 10/3b374666a85ea3ca43fa49aa3a048d21c9b475c96eb13c133505d2324e7ae5efd6a454f41efe46a152269e9b6a00c9edbe63ec7fa1921957165aae16625acd67 + languageName: node + linkType: hard + "ci-info@npm:^3.2.0": version: 3.9.0 resolution: "ci-info@npm:3.9.0" @@ -15275,6 +15305,17 @@ __metadata: languageName: node linkType: hard +"is-ci@npm:^2.0.0": + version: 2.0.0 + resolution: "is-ci@npm:2.0.0" + dependencies: + ci-info: "npm:^2.0.0" + bin: + is-ci: bin.js + checksum: 10/77b869057510f3efa439bbb36e9be429d53b3f51abd4776eeea79ab3b221337fe1753d1e50058a9e2c650d38246108beffb15ccfd443929d77748d8c0cc90144 + languageName: node + linkType: hard + "is-core-module@npm:^2.13.0, is-core-module@npm:^2.16.1": version: 2.16.1 resolution: "is-core-module@npm:2.16.1" @@ -16958,6 +16999,16 @@ __metadata: languageName: node linkType: hard +"jest-silent-reporter@npm:^0.6.0": + version: 0.6.0 + resolution: "jest-silent-reporter@npm:0.6.0" + dependencies: + chalk: "npm:^4.0.0" + jest-util: "npm:^26.0.0" + checksum: 10/443e0abaf5a6dc8c17da1e8495b7a55f813224adc39b6d1954bf49ff7fe70533b1020571453180dbb8388ace87f8e1dfc79610a4554bb93334f6c4154231c292 + languageName: node + linkType: hard + "jest-snapshot@npm:30.1.2": version: 30.1.2 resolution: "jest-snapshot@npm:30.1.2" @@ -17086,6 +17137,20 @@ __metadata: languageName: node linkType: hard +"jest-util@npm:^26.0.0": + version: 26.6.2 + resolution: "jest-util@npm:26.6.2" + dependencies: + "@jest/types": "npm:^26.6.2" + "@types/node": "npm:*" + chalk: "npm:^4.0.0" + graceful-fs: "npm:^4.2.4" + is-ci: "npm:^2.0.0" + micromatch: "npm:^4.0.2" + checksum: 10/4502bc699f147d2fa43274af18174b55fd5b956becd1347665217e35a5354e929206abaef580f967ed239587be926c835eb3ca9b5c361205df1988bc8d58a462 + languageName: node + linkType: hard + "jest-util@npm:^29.7.0": version: 29.7.0 resolution: "jest-util@npm:29.7.0" From ca3d46485974333d32eae2fb2596c945d25d410f Mon Sep 17 00:00:00 2001 From: Adam Haeger Date: Mon, 18 May 2026 14:47:30 +0200 Subject: [PATCH 08/13] moved Input to lib --- libs/form-component/package.json | 1 + .../app-components/Input/FormattedInput.tsx | 11 ++ .../src/app-components/Input/Input.module.css | 0 .../app-components/Input/Input.stories.tsx | 69 +++++++++ .../src/app-components/Input/Input.test.tsx | 141 ++++++++++++++++++ .../src/app-components/Input/Input.tsx | 46 ++---- .../src/app-components/Input/NumericInput.tsx | 9 ++ .../src/app-components/Input/constants.ts | 0 .../src/app-components/Input/index.ts | 6 + .../src/app-components/index.ts | 1 + src/App/frontend/monorepo-changed-paths.txt | 3 +- .../app-components/Input/FormattedInput.tsx | 15 -- .../src/app-components/Input/NumericInput.tsx | 13 -- .../components/DevToolsLogs/DevToolsLogs.tsx | 10 +- .../instantiate/containers/PartySelection.tsx | 8 +- .../src/layout/Address/AddressComponent.tsx | 3 +- .../src/layout/Input/InputComponent.tsx | 29 ++-- src/App/frontend/src/layout/Input/config.ts | 3 +- .../OrganisationLookupComponent.tsx | 6 +- .../PersonLookup/PersonLookupComponent.tsx | 9 +- 20 files changed, 283 insertions(+), 100 deletions(-) create mode 100644 libs/form-component/src/app-components/Input/FormattedInput.tsx rename {src/App/frontend => libs/form-component}/src/app-components/Input/Input.module.css (100%) create mode 100644 libs/form-component/src/app-components/Input/Input.stories.tsx create mode 100644 libs/form-component/src/app-components/Input/Input.test.tsx rename {src/App/frontend => libs/form-component}/src/app-components/Input/Input.tsx (61%) create mode 100644 libs/form-component/src/app-components/Input/NumericInput.tsx rename {src/App/frontend => libs/form-component}/src/app-components/Input/constants.ts (100%) create mode 100644 libs/form-component/src/app-components/Input/index.ts delete mode 100644 src/App/frontend/src/app-components/Input/FormattedInput.tsx delete mode 100644 src/App/frontend/src/app-components/Input/NumericInput.tsx diff --git a/libs/form-component/package.json b/libs/form-component/package.json index c68dc8da2b8..b367aca9a7d 100644 --- a/libs/form-component/package.json +++ b/libs/form-component/package.json @@ -5,6 +5,7 @@ "main": "./src/index.ts", "exports": { ".": "./src/index.ts", + "./src/app-components/Input/constants": "./src/app-components/Input/constants.ts", "./styles/global.css": "./src/styles/global.css" }, "private": true, diff --git a/libs/form-component/src/app-components/Input/FormattedInput.tsx b/libs/form-component/src/app-components/Input/FormattedInput.tsx new file mode 100644 index 00000000000..383f7ae0068 --- /dev/null +++ b/libs/form-component/src/app-components/Input/FormattedInput.tsx @@ -0,0 +1,11 @@ +import { PatternFormat } from 'react-number-format'; +import type { PatternFormatProps } from 'react-number-format'; + +import { Input } from './Input'; +import type { InputProps } from './Input'; + +export function FormattedInput( + props: Omit & InputProps, +) { + return ; +} diff --git a/src/App/frontend/src/app-components/Input/Input.module.css b/libs/form-component/src/app-components/Input/Input.module.css similarity index 100% rename from src/App/frontend/src/app-components/Input/Input.module.css rename to libs/form-component/src/app-components/Input/Input.module.css diff --git a/libs/form-component/src/app-components/Input/Input.stories.tsx b/libs/form-component/src/app-components/Input/Input.stories.tsx new file mode 100644 index 00000000000..7f793256cde --- /dev/null +++ b/libs/form-component/src/app-components/Input/Input.stories.tsx @@ -0,0 +1,69 @@ +import type { Meta, StoryObj } from '@storybook/react-vite'; + +import { Input } from './Input'; + +const meta = { + title: 'AppComponents/Input', + component: Input, + args: { + label: 'First name', + }, +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const Preview: Story = {}; + +export const WithPlaceholder: Story = { + args: { + placeholder: 'Type your name', + }, +}; + +export const WithPrefixAndSuffix: Story = { + args: { + label: 'Amount', + prefix: 'NOK', + suffix: ',-', + }, +}; + +export const Search: Story = { + args: { + label: 'Search', + type: 'search', + }, +}; + +export const WithError: Story = { + args: { + error: true, + }, +}; + +export const ReadOnly: Story = { + args: { + value: 'Ada Lovelace', + readOnly: true, + }, +}; + +export const TextOnly: Story = { + args: { + value: 'Ada Lovelace', + textonly: true, + }, +}; + +export const WithCharacterLimit: Story = { + args: { + label: 'Short bio', + characterLimit: { + limit: 50, + under: 'characters remaining', + over: 'characters too many', + }, + }, +}; diff --git a/libs/form-component/src/app-components/Input/Input.test.tsx b/libs/form-component/src/app-components/Input/Input.test.tsx new file mode 100644 index 00000000000..649655839ff --- /dev/null +++ b/libs/form-component/src/app-components/Input/Input.test.tsx @@ -0,0 +1,141 @@ +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import { FormattedInput } from './FormattedInput'; +import { Input } from './Input'; +import { NumericInput } from './NumericInput'; + +describe('Input', () => { + it('renders a textbox with a visible label', () => { + render(); + + expect(screen.getByRole('textbox', { name: 'First name' })).toBeInTheDocument(); + }); + + it('uses aria-label as the accessible name', () => { + render(); + + expect(screen.getByRole('textbox', { name: 'Search' })).toBeInTheDocument(); + }); + + it('uses aria-labelledby as the accessible name', () => { + render( + <> + Social security number + + , + ); + + expect(screen.getByRole('textbox', { name: 'Social security number' })).toBeInTheDocument(); + }); + + it('forwards change events', async () => { + const user = userEvent.setup(); + const onChange = vi.fn(); + render(); + + await user.type(screen.getByRole('textbox', { name: 'Name' }), 'Ada'); + + expect(onChange).toHaveBeenCalled(); + expect(screen.getByRole('textbox', { name: 'Name' })).toHaveValue('Ada'); + }); + + it('renders a read-only input', () => { + render( {}} />); + + expect(screen.getByRole('textbox', { name: 'Name' })).toHaveAttribute('readonly'); + }); + + it('marks the input as invalid when an error is passed', () => { + render(); + + expect(screen.getByRole('textbox', { name: 'Name' })).toHaveAttribute('aria-invalid', 'true'); + }); + + it('renders prefix and suffix as already-translated strings', () => { + render(); + + expect(screen.getByText('NOK')).toBeInTheDocument(); + expect(screen.getByText('per month')).toBeInTheDocument(); + }); + + it('renders a placeholder', () => { + render(); + + expect(screen.getByPlaceholderText('Type to search')).toBeInTheDocument(); + }); + + it('shows a character counter when characterLimit is set', () => { + render( + {}} + characterLimit={{ limit: 10, under: 'characters left', over: 'too many characters' }} + />, + ); + + expect(screen.getByText(/characters left/i)).toBeInTheDocument(); + }); + + it('hides the character counter for read-only inputs', () => { + render( + {}} + characterLimit={{ limit: 10, under: 'characters left', over: 'too many characters' }} + />, + ); + + expect(screen.queryByText(/characters left/i)).not.toBeInTheDocument(); + }); + + describe('textonly', () => { + it('renders the value as plain text instead of an input', () => { + render( {}} />); + + expect(screen.getByText('Ada Lovelace')).toBeInTheDocument(); + expect(screen.queryByRole('textbox')).not.toBeInTheDocument(); + }); + + it('renders nothing when the value is empty', () => { + const { container } = render( + {}} />, + ); + + expect(container).toBeEmptyDOMElement(); + }); + }); +}); + +describe('FormattedInput', () => { + it('formats the value according to the pattern', () => { + render( + {}} + />, + ); + + expect(screen.getByRole('textbox', { name: 'Phone' })).toHaveValue('123 45 678'); + }); +}); + +describe('NumericInput', () => { + it('formats the value with a thousand separator', () => { + render( + {}} + />, + ); + + expect(screen.getByRole('textbox', { name: 'Amount' })).toHaveValue('1 234 567'); + }); +}); diff --git a/src/App/frontend/src/app-components/Input/Input.tsx b/libs/form-component/src/app-components/Input/Input.tsx similarity index 61% rename from src/App/frontend/src/app-components/Input/Input.tsx rename to libs/form-component/src/app-components/Input/Input.tsx index 29ef419d86f..4257b9b7495 100644 --- a/src/App/frontend/src/app-components/Input/Input.tsx +++ b/libs/form-component/src/app-components/Input/Input.tsx @@ -4,45 +4,26 @@ import type { InputHTMLAttributes, ReactNode } from 'react'; import { Paragraph, Textfield } from '@digdir/designsystemet-react'; import type { FieldCounterProps } from '@digdir/designsystemet-react'; -import { useTranslation } from 'src/app-components/AppComponentsProvider'; -import classes from 'src/app-components/Input/Input.module.css'; -import type { InputType } from 'src/app-components/Input/constants'; -import type { TranslationKey } from 'src/app-components/types'; - -/** - * Hook to create a character limit object for use in input components - */ -export const useCharacterLimit = (maxLength: number | undefined): FieldCounterProps | undefined => { - const { translate } = useTranslation(); - - if (maxLength === undefined) { - return undefined; - } - - return { - limit: maxLength, - under: translate('input_components.remaining_characters'), - over: translate('input_components.exceeded_max_limit'), - }; -}; +import classes from './Input.module.css'; +import type { InputType } from './constants'; type LabelRequired = - | { 'aria-label': TranslationKey; 'aria-labelledby'?: never; label?: never } + | { 'aria-label': string; 'aria-labelledby'?: never; label?: never } | { 'aria-label'?: never; 'aria-labelledby'?: never; label: ReactNode } | { 'aria-label'?: never; 'aria-labelledby': string; label?: never }; export type InputProps = { size?: 'sm' | 'md' | 'lg'; - prefix?: TranslationKey; - suffix?: TranslationKey; + prefix?: string; + suffix?: string; error?: ReactNode; disabled?: boolean; id?: string; readOnly?: boolean; type?: InputType; textonly?: boolean; - maxLength?: number; - placeholder?: TranslationKey; + characterLimit?: FieldCounterProps; + placeholder?: string; } & Pick< InputHTMLAttributes, | 'value' @@ -65,7 +46,7 @@ export function Input(props: InputProps) { readOnly, error, textonly, - maxLength, + characterLimit, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, label, @@ -75,9 +56,6 @@ export function Input(props: InputProps) { ...rest } = props; - const characterLimit = useCharacterLimit(maxLength); - const { translate } = useTranslation(); - const handlePaste = (event: React.ClipboardEvent) => { if (readOnly) { event.preventDefault(); @@ -103,7 +81,7 @@ export function Input(props: InputProps) { } const labelProps = ariaLabel - ? { 'aria-label': translate(ariaLabel) } + ? { 'aria-label': ariaLabel } : ariaLabelledBy ? { 'aria-labelledby': ariaLabelledBy } : { label }; @@ -115,9 +93,9 @@ export function Input(props: InputProps) { aria-invalid={!!error} readOnly={readOnly} counter={!readOnly ? characterLimit : undefined} - prefix={prefix ? translate(prefix) : undefined} - suffix={suffix ? translate(suffix) : undefined} - placeholder={placeholder ? translate(placeholder) : undefined} + prefix={prefix} + suffix={suffix} + placeholder={placeholder} {...labelProps} {...rest} /> diff --git a/libs/form-component/src/app-components/Input/NumericInput.tsx b/libs/form-component/src/app-components/Input/NumericInput.tsx new file mode 100644 index 00000000000..bd2410ef55d --- /dev/null +++ b/libs/form-component/src/app-components/Input/NumericInput.tsx @@ -0,0 +1,9 @@ +import { NumericFormat } from 'react-number-format'; +import type { NumericFormatProps } from 'react-number-format'; + +import { Input } from './Input'; +import type { InputProps } from './Input'; + +export function NumericInput(props: Omit & InputProps) { + return ; +} diff --git a/src/App/frontend/src/app-components/Input/constants.ts b/libs/form-component/src/app-components/Input/constants.ts similarity index 100% rename from src/App/frontend/src/app-components/Input/constants.ts rename to libs/form-component/src/app-components/Input/constants.ts diff --git a/libs/form-component/src/app-components/Input/index.ts b/libs/form-component/src/app-components/Input/index.ts new file mode 100644 index 00000000000..96168b1d3bc --- /dev/null +++ b/libs/form-component/src/app-components/Input/index.ts @@ -0,0 +1,6 @@ +export { Input } from './Input'; +export type { InputProps } from './Input'; +export { FormattedInput } from './FormattedInput'; +export { NumericInput } from './NumericInput'; +export { EXTERNAL_INPUT_TYPE, INPUT_AUTO_COMPLETE } from './constants'; +export type { InputType } from './constants'; diff --git a/libs/form-component/src/app-components/index.ts b/libs/form-component/src/app-components/index.ts index 33e52766455..78d36544f77 100644 --- a/libs/form-component/src/app-components/index.ts +++ b/libs/form-component/src/app-components/index.ts @@ -3,6 +3,7 @@ export * from './Button'; export * from './Card'; export * from './Datepicker'; export * from './Flex'; +export * from './Input'; export * from './hooks'; export * from './DisplayDate'; export * from './Spinner'; diff --git a/src/App/frontend/monorepo-changed-paths.txt b/src/App/frontend/monorepo-changed-paths.txt index 7edd894c8d9..a1f219be1e2 100644 --- a/src/App/frontend/monorepo-changed-paths.txt +++ b/src/App/frontend/monorepo-changed-paths.txt @@ -2,7 +2,7 @@ .github/ .husky/ .yarn/patches/jsdom-npm-26.1.0-3857255f02.patch -.yarn/releases/yarn-4.14.1.cjs +.yarn/releases/yarn-4.12.0.cjs .yarnrc.yml LICENSE.md adr/001-component-library.md @@ -18,6 +18,7 @@ src/app-components/Card/ src/app-components/Date/ src/app-components/Datepicker/ src/app-components/Flex/ +src/app-components/Input/ src/app-components/Text/DisplayText.tsx src/app-components/loading/Spinner/Spinner.tsx src/codegen/schemas/layout-sets.schema.v1.ts diff --git a/src/App/frontend/src/app-components/Input/FormattedInput.tsx b/src/App/frontend/src/app-components/Input/FormattedInput.tsx deleted file mode 100644 index cb6efae676d..00000000000 --- a/src/App/frontend/src/app-components/Input/FormattedInput.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react'; -import { PatternFormat } from 'react-number-format'; -import type { PatternFormatProps } from 'react-number-format'; - -import { Input } from 'src/app-components/Input/Input'; -import type { InputProps } from 'src/app-components/Input/Input'; - -export function FormattedInput(props: Omit & InputProps) { - return ( - - ); -} diff --git a/src/App/frontend/src/app-components/Input/NumericInput.tsx b/src/App/frontend/src/app-components/Input/NumericInput.tsx deleted file mode 100644 index 097ed0ddf1a..00000000000 --- a/src/App/frontend/src/app-components/Input/NumericInput.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; -import { NumericFormat, type NumericFormatProps } from 'react-number-format'; - -import { Input, type InputProps } from 'src/app-components/Input/Input'; - -export function NumericInput(props: Omit & InputProps) { - return ( - - ); -} diff --git a/src/App/frontend/src/features/devtools/components/DevToolsLogs/DevToolsLogs.tsx b/src/App/frontend/src/features/devtools/components/DevToolsLogs/DevToolsLogs.tsx index 7df146973df..235d936950d 100644 --- a/src/App/frontend/src/features/devtools/components/DevToolsLogs/DevToolsLogs.tsx +++ b/src/App/frontend/src/features/devtools/components/DevToolsLogs/DevToolsLogs.tsx @@ -1,6 +1,6 @@ import React, { useState } from 'react'; -import { Button } from '@app/form-component'; +import { Button, Input } from '@app/form-component'; import { DownloadIcon, ExclamationmarkTriangleFillIcon, @@ -9,10 +9,9 @@ import { XMarkOctagonFillIcon, } from '@navikt/aksel-icons'; -import { Input } from 'src/app-components/Input/Input'; -import { translationKey } from 'src/AppComponentsBridge'; import classes from 'src/features/devtools/components/DevToolsLogs/DevToolsLogs.module.css'; import { useDevToolsStore } from 'src/features/devtools/data/DevToolsStore'; +import { useLanguage } from 'src/features/language/useLanguage'; const colorMap = { error: 'red', @@ -21,6 +20,7 @@ const colorMap = { }; export const DevToolsLogs = () => { + const { langAsString } = useLanguage(); const logs = useDevToolsStore((state) => state.logs); const [filter, setFilter] = useState(''); const [showLevels, setShowLevels] = useState({ error: true, warn: true, info: true }); @@ -72,10 +72,10 @@ export const DevToolsLogs = () => {
setFilter(e.target.value)} - placeholder={translationKey('devtools.filter_logs')} + placeholder={langAsString('devtools.filter_logs')} />
diff --git a/src/App/frontend/src/features/instantiate/containers/PartySelection.tsx b/src/App/frontend/src/features/instantiate/containers/PartySelection.tsx index 53e87d208fc..52cff1d60e7 100644 --- a/src/App/frontend/src/features/instantiate/containers/PartySelection.tsx +++ b/src/App/frontend/src/features/instantiate/containers/PartySelection.tsx @@ -1,13 +1,11 @@ import React from 'react'; import { useMatch, useNavigate } from 'react-router'; -import { Button, Flex } from '@app/form-component'; +import { Button, Flex, Input } from '@app/form-component'; import { Checkbox, Heading, Paragraph } from '@digdir/designsystemet-react'; import { PlusIcon } from '@navikt/aksel-icons'; import cn from 'classnames'; -import { Input } from 'src/app-components/Input/Input'; -import { translationKey } from 'src/AppComponentsBridge'; import { AltinnParty } from 'src/components/altinnParty'; import { useAppName, useAppOwner } from 'src/core/texts/appTexts'; import { getApplicationMetadata } from 'src/features/applicationMetadata'; @@ -141,8 +139,8 @@ export const PartySelection = () => { > (undefined); const formValue = localValue ?? realFormValue; @@ -146,7 +147,7 @@ const InputVariant = ({ }); const labelProps = textResourceBindings?.title - ? { 'aria-label': translationKey(textResourceBindings?.title) } + ? { 'aria-label': langAsString(textResourceBindings.title) } : { 'aria-labelledby': labelId }; const inputProps: InputProps = { @@ -160,8 +161,8 @@ const InputVariant = ({ required, onBlur: () => debounce('blur'), error: !useIsValid(baseComponentId), - prefix: translationKey(textResourceBindings?.prefix), - suffix: translationKey(textResourceBindings?.suffix), + prefix: textResourceBindings?.prefix ? langAsString(textResourceBindings.prefix) : undefined, + suffix: textResourceBindings?.suffix ? langAsString(textResourceBindings.suffix) : undefined, style: { width: '100%' }, inputMode, pattern, @@ -178,7 +179,7 @@ const InputVariant = ({ onChange={(event) => { setValue('simpleBinding', event.target.value); }} - maxLength={maxLength} + characterLimit={characterLimit} /> ); case 'pattern': @@ -194,7 +195,7 @@ const InputVariant = ({ } setValue('simpleBinding', values.value); }} - maxLength={maxLength} + characterLimit={characterLimit} /> ); case 'number': @@ -202,8 +203,8 @@ const InputVariant = ({ { @@ -251,7 +252,7 @@ const InputVariant = ({ setValue('simpleBinding', pastedText); } }} - maxLength={maxLength} + characterLimit={characterLimit} /> ); } diff --git a/src/App/frontend/src/layout/Input/config.ts b/src/App/frontend/src/layout/Input/config.ts index 4f59ae502b3..80685944d57 100644 --- a/src/App/frontend/src/layout/Input/config.ts +++ b/src/App/frontend/src/layout/Input/config.ts @@ -1,4 +1,5 @@ -import { EXTERNAL_INPUT_TYPE, INPUT_AUTO_COMPLETE } from 'src/app-components/Input/constants'; +import { EXTERNAL_INPUT_TYPE, INPUT_AUTO_COMPLETE } from '@app/form-component/src/app-components/Input/constants'; + import { CG } from 'src/codegen/CG'; import { CompCategory } from 'src/layout/common'; diff --git a/src/App/frontend/src/layout/OrganisationLookup/OrganisationLookupComponent.tsx b/src/App/frontend/src/layout/OrganisationLookup/OrganisationLookupComponent.tsx index b6af7712d3f..47f2604f5e2 100644 --- a/src/App/frontend/src/layout/OrganisationLookup/OrganisationLookupComponent.tsx +++ b/src/App/frontend/src/layout/OrganisationLookup/OrganisationLookupComponent.tsx @@ -1,15 +1,13 @@ import React, { useState } from 'react'; -import { Button } from '@app/form-component'; +import { Button, NumericInput } from '@app/form-component'; import { Field, Paragraph, ValidationMessage } from '@digdir/designsystemet-react'; import { queryOptions, useQuery } from '@tanstack/react-query'; import type { PropsFromGenericComponent } from '..'; -import { NumericInput } from 'src/app-components/Input/NumericInput'; import { Fieldset } from 'src/app-components/Label/Fieldset'; import { Label } from 'src/app-components/Label/Label'; -import { translationKey } from 'src/AppComponentsBridge'; import { Description } from 'src/components/form/Description'; import { RequiredIndicator } from 'src/components/form/RequiredIndicator'; import { getDescriptionId } from 'src/components/label/Label'; @@ -154,7 +152,7 @@ export function OrganisationLookupComponent({ Date: Mon, 18 May 2026 16:02:31 +0200 Subject: [PATCH 09/13] removed unused IGridStyling.xl size --- libs/form-component/src/app-components/Flex/Flex.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/libs/form-component/src/app-components/Flex/Flex.tsx b/libs/form-component/src/app-components/Flex/Flex.tsx index b14a2fb1e63..2c6629fdf10 100644 --- a/libs/form-component/src/app-components/Flex/Flex.tsx +++ b/libs/form-component/src/app-components/Flex/Flex.tsx @@ -12,7 +12,6 @@ export interface IGridStyling { sm?: GridSize; md?: GridSize; lg?: GridSize; - xl?: GridSize; } type Spacing = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10; From bf79f00c009edea41ebae3306a6b0fa6e1b1dbde Mon Sep 17 00:00:00 2001 From: Adam Haeger Date: Tue, 19 May 2026 14:02:29 +0200 Subject: [PATCH 10/13] moved constants to layout component --- libs/form-component/package.json | 1 - libs/form-component/src/app-components/Flex/Flex.tsx | 1 + .../form-component/src/app-components/Input/Input.tsx | 11 +++++++---- libs/form-component/src/app-components/Input/index.ts | 3 +-- src/App/frontend/src/layout/Input/config.ts | 3 +-- .../App/frontend/src/layout}/Input/constants.ts | 0 6 files changed, 10 insertions(+), 9 deletions(-) rename {libs/form-component/src/app-components => src/App/frontend/src/layout}/Input/constants.ts (100%) diff --git a/libs/form-component/package.json b/libs/form-component/package.json index b367aca9a7d..c68dc8da2b8 100644 --- a/libs/form-component/package.json +++ b/libs/form-component/package.json @@ -5,7 +5,6 @@ "main": "./src/index.ts", "exports": { ".": "./src/index.ts", - "./src/app-components/Input/constants": "./src/app-components/Input/constants.ts", "./styles/global.css": "./src/styles/global.css" }, "private": true, diff --git a/libs/form-component/src/app-components/Flex/Flex.tsx b/libs/form-component/src/app-components/Flex/Flex.tsx index 2c6629fdf10..b14a2fb1e63 100644 --- a/libs/form-component/src/app-components/Flex/Flex.tsx +++ b/libs/form-component/src/app-components/Flex/Flex.tsx @@ -12,6 +12,7 @@ export interface IGridStyling { sm?: GridSize; md?: GridSize; lg?: GridSize; + xl?: GridSize; } type Spacing = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10; diff --git a/libs/form-component/src/app-components/Input/Input.tsx b/libs/form-component/src/app-components/Input/Input.tsx index 4257b9b7495..ab5dd1c2aa3 100644 --- a/libs/form-component/src/app-components/Input/Input.tsx +++ b/libs/form-component/src/app-components/Input/Input.tsx @@ -1,11 +1,14 @@ import React from 'react'; import type { InputHTMLAttributes, ReactNode } from 'react'; -import { Paragraph, Textfield } from '@digdir/designsystemet-react'; -import type { FieldCounterProps } from '@digdir/designsystemet-react'; +import { + Paragraph, + Textfield, + TextfieldProps, + FieldCounterProps, +} from '@digdir/designsystemet-react'; import classes from './Input.module.css'; -import type { InputType } from './constants'; type LabelRequired = | { 'aria-label': string; 'aria-labelledby'?: never; label?: never } @@ -20,7 +23,7 @@ export type InputProps = { disabled?: boolean; id?: string; readOnly?: boolean; - type?: InputType; + type?: TextfieldProps['type']; textonly?: boolean; characterLimit?: FieldCounterProps; placeholder?: string; diff --git a/libs/form-component/src/app-components/Input/index.ts b/libs/form-component/src/app-components/Input/index.ts index 96168b1d3bc..cff21c209c8 100644 --- a/libs/form-component/src/app-components/Input/index.ts +++ b/libs/form-component/src/app-components/Input/index.ts @@ -2,5 +2,4 @@ export { Input } from './Input'; export type { InputProps } from './Input'; export { FormattedInput } from './FormattedInput'; export { NumericInput } from './NumericInput'; -export { EXTERNAL_INPUT_TYPE, INPUT_AUTO_COMPLETE } from './constants'; -export type { InputType } from './constants'; + diff --git a/src/App/frontend/src/layout/Input/config.ts b/src/App/frontend/src/layout/Input/config.ts index 80685944d57..f97c547d941 100644 --- a/src/App/frontend/src/layout/Input/config.ts +++ b/src/App/frontend/src/layout/Input/config.ts @@ -1,7 +1,6 @@ -import { EXTERNAL_INPUT_TYPE, INPUT_AUTO_COMPLETE } from '@app/form-component/src/app-components/Input/constants'; - import { CG } from 'src/codegen/CG'; import { CompCategory } from 'src/layout/common'; +import { EXTERNAL_INPUT_TYPE, INPUT_AUTO_COMPLETE } from 'src/layout/Input/constants'; export const Config = new CG.component({ category: CompCategory.Form, diff --git a/libs/form-component/src/app-components/Input/constants.ts b/src/App/frontend/src/layout/Input/constants.ts similarity index 100% rename from libs/form-component/src/app-components/Input/constants.ts rename to src/App/frontend/src/layout/Input/constants.ts From 4d707fcafb9bce27b4b48775c71e351d8cb76f86 Mon Sep 17 00:00:00 2001 From: Adam Haeger Date: Tue, 19 May 2026 15:40:21 +0200 Subject: [PATCH 11/13] fixed unused --- .../src/features/instantiate/containers/PartySelection.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/App/frontend/src/features/instantiate/containers/PartySelection.tsx b/src/App/frontend/src/features/instantiate/containers/PartySelection.tsx index 372b501afcd..52cff1d60e7 100644 --- a/src/App/frontend/src/features/instantiate/containers/PartySelection.tsx +++ b/src/App/frontend/src/features/instantiate/containers/PartySelection.tsx @@ -6,7 +6,6 @@ import { Checkbox, Heading, Paragraph } from '@digdir/designsystemet-react'; import { PlusIcon } from '@navikt/aksel-icons'; import cn from 'classnames'; -import { translationKey } from 'src/AppComponentsBridge'; import { AltinnParty } from 'src/components/altinnParty'; import { useAppName, useAppOwner } from 'src/core/texts/appTexts'; import { getApplicationMetadata } from 'src/features/applicationMetadata'; From 0828395c1f91e42ca7dca1f2d8286fd03d8c40a7 Mon Sep 17 00:00:00 2001 From: Adam Haeger Date: Wed, 20 May 2026 12:24:23 +0200 Subject: [PATCH 12/13] cleanup after code review --- libs/form-component/src/app-components/Input/Input.tsx | 4 ++-- src/App/frontend/src/layout/Input/InputComponent.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libs/form-component/src/app-components/Input/Input.tsx b/libs/form-component/src/app-components/Input/Input.tsx index ab5dd1c2aa3..af5c6c15cde 100644 --- a/libs/form-component/src/app-components/Input/Input.tsx +++ b/libs/form-component/src/app-components/Input/Input.tsx @@ -4,8 +4,8 @@ import type { InputHTMLAttributes, ReactNode } from 'react'; import { Paragraph, Textfield, - TextfieldProps, - FieldCounterProps, + type TextfieldProps, + type FieldCounterProps, } from '@digdir/designsystemet-react'; import classes from './Input.module.css'; diff --git a/src/App/frontend/src/layout/Input/InputComponent.tsx b/src/App/frontend/src/layout/Input/InputComponent.tsx index 6c5f28a98fb..9cc1dc17041 100644 --- a/src/App/frontend/src/layout/Input/InputComponent.tsx +++ b/src/App/frontend/src/layout/Input/InputComponent.tsx @@ -203,8 +203,8 @@ const InputVariant = ({ { From 2232c5ec5764b832fd4f57ea54f8f68d8a3af2d1 Mon Sep 17 00:00:00 2001 From: Adam Haeger Date: Wed, 20 May 2026 12:29:27 +0200 Subject: [PATCH 13/13] moved FormattedInput and NumericInput to sepearate files --- .../Input/FormattedInput.test.tsx | 18 +++++++++++ .../src/app-components/Input/Input.test.tsx | 32 ------------------- .../Input/NumericInput.test.tsx | 18 +++++++++++ 3 files changed, 36 insertions(+), 32 deletions(-) create mode 100644 libs/form-component/src/app-components/Input/FormattedInput.test.tsx create mode 100644 libs/form-component/src/app-components/Input/NumericInput.test.tsx diff --git a/libs/form-component/src/app-components/Input/FormattedInput.test.tsx b/libs/form-component/src/app-components/Input/FormattedInput.test.tsx new file mode 100644 index 00000000000..70cce0160b7 --- /dev/null +++ b/libs/form-component/src/app-components/Input/FormattedInput.test.tsx @@ -0,0 +1,18 @@ +import { render, screen } from '@testing-library/react'; + +import { FormattedInput } from './FormattedInput'; + +describe('FormattedInput', () => { + it('formats the value according to the pattern', () => { + render( + {}} + />, + ); + + expect(screen.getByRole('textbox', { name: 'Phone' })).toHaveValue('123 45 678'); + }); +}); diff --git a/libs/form-component/src/app-components/Input/Input.test.tsx b/libs/form-component/src/app-components/Input/Input.test.tsx index 649655839ff..b12a20c259a 100644 --- a/libs/form-component/src/app-components/Input/Input.test.tsx +++ b/libs/form-component/src/app-components/Input/Input.test.tsx @@ -1,9 +1,7 @@ import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { FormattedInput } from './FormattedInput'; import { Input } from './Input'; -import { NumericInput } from './NumericInput'; describe('Input', () => { it('renders a textbox with a visible label', () => { @@ -109,33 +107,3 @@ describe('Input', () => { }); }); }); - -describe('FormattedInput', () => { - it('formats the value according to the pattern', () => { - render( - {}} - />, - ); - - expect(screen.getByRole('textbox', { name: 'Phone' })).toHaveValue('123 45 678'); - }); -}); - -describe('NumericInput', () => { - it('formats the value with a thousand separator', () => { - render( - {}} - />, - ); - - expect(screen.getByRole('textbox', { name: 'Amount' })).toHaveValue('1 234 567'); - }); -}); diff --git a/libs/form-component/src/app-components/Input/NumericInput.test.tsx b/libs/form-component/src/app-components/Input/NumericInput.test.tsx new file mode 100644 index 00000000000..6676f1cded1 --- /dev/null +++ b/libs/form-component/src/app-components/Input/NumericInput.test.tsx @@ -0,0 +1,18 @@ +import { render, screen } from '@testing-library/react'; + +import { NumericInput } from './NumericInput'; + +describe('NumericInput', () => { + it('formats the value with a thousand separator', () => { + render( + {}} + />, + ); + + expect(screen.getByRole('textbox', { name: 'Amount' })).toHaveValue('1 234 567'); + }); +});