11import without from 'lodash/without' ;
2- import { FC } from 'react' ;
3- import { useController } from 'react-hook-form' ;
2+ import { FC , ReactElement } from 'react' ;
3+ import { FieldPathByValue , FieldValues , useController } from 'react-hook-form' ;
44import { useTranslation } from 'react-i18next' ;
55import { twJoin , twMerge } from 'tailwind-merge' ;
6- import { mapStopRegistryTransportModeTypeToUiName } from '../../../../../../i18n/uiNameMappings' ;
7- import { Row } from '../../../../../../layoutComponents' ;
8- import { JoreStopRegistryTransportModeType } from '../../../../../../types/stop-registry' ;
9- import { AllOptionEnum } from '../../../../../../utils' ;
10- import { StopSearchFilters } from '../../../types' ;
11- import { stopSearchBarTestIds } from '../stopSearchBarTestIds' ;
12- import { DisableableFilterProps } from '../Types/DisableableFilterProps' ;
6+ import { TranslationKey } from '../../../../i18n' ;
7+ import { mapStopRegistryTransportModeTypeToUiName } from '../../../../i18n/uiNameMappings' ;
8+ import { Row } from '../../../../layoutComponents' ;
9+ import { JoreStopRegistryTransportModeType } from '../../../../types/stop-registry' ;
10+ import { AllOptionEnum } from '../../../../utils' ;
1311import s from './TransportationModeFilter.module.css' ;
1412
13+ const testIds = {
14+ transportationModeButton : (
15+ prefix : string ,
16+ mode : JoreStopRegistryTransportModeType ,
17+ ) => `${ prefix } ::transportationMode::${ mode } ` ,
18+ } ;
19+
1520const modeIconMap : Readonly < Record < JoreStopRegistryTransportModeType , string > > =
1621 {
1722 [ JoreStopRegistryTransportModeType . Bus ] : 'icon-bus' ,
@@ -25,12 +30,14 @@ type TransportationModeButtonProps = {
2530 readonly isSelected : ( mode : JoreStopRegistryTransportModeType ) => boolean ;
2631 readonly mode : JoreStopRegistryTransportModeType ;
2732 readonly onToggle : ( mode : JoreStopRegistryTransportModeType ) => void ;
33+ readonly testIdPrefix : string ;
2834} ;
2935
3036const TransportationModeButton : FC < TransportationModeButtonProps > = ( {
3137 isSelected,
3238 mode,
3339 onToggle,
40+ testIdPrefix,
3441} ) => {
3542 const { t } = useTranslation ( ) ;
3643
@@ -44,7 +51,7 @@ const TransportationModeButton: FC<TransportationModeButtonProps> = ({
4451 'aria-checked:border-tweaked-brand aria-checked:bg-tweaked-brand aria-checked:text-white' ,
4552 modeIconMap [ mode ] ,
4653 ) }
47- data-testid = { stopSearchBarTestIds . transportationModeButton ( mode ) }
54+ data-testid = { testIds . transportationModeButton ( testIdPrefix , mode ) }
4855 onClick = { ( ) => onToggle ( mode ) }
4956 role = "checkbox"
5057 type = "button"
@@ -60,35 +67,54 @@ const options: ReadonlyArray<JoreStopRegistryTransportModeType> = [
6067 JoreStopRegistryTransportModeType . Metro ,
6168] ;
6269
63- export const TransportationModeFilter : FC < DisableableFilterProps > = ( {
70+ type TransportationModeFilterProps < FormState extends FieldValues > = {
71+ readonly fieldPath : FieldPathByValue <
72+ FormState ,
73+ ReadonlyArray < JoreStopRegistryTransportModeType | AllOptionEnum >
74+ > ;
75+ readonly translationPrefix : TranslationKey ;
76+ readonly testIdPrefix : string ;
77+ readonly className ?: string ;
78+ readonly disabled ?: boolean ;
79+ } ;
80+
81+ export const TransportationModeFilter = < FormState extends FieldValues > ( {
82+ fieldPath,
83+ translationPrefix,
84+ testIdPrefix,
6485 className,
6586 disabled,
66- } ) => {
87+ } : TransportationModeFilterProps < FormState > ) : ReactElement => {
6788 const { t } = useTranslation ( ) ;
6889
6990 const {
7091 field : { value, onBlur, onChange } ,
71- } = useController < StopSearchFilters , 'transportationMode' > ( {
72- name : 'transportationMode' ,
92+ } = useController < FormState , typeof fieldPath > ( {
93+ name : fieldPath ,
7394 disabled,
7495 } ) ;
7596
97+ // Type assertion to help TypeScript understand the correct type
98+ const typedValue = value as ReadonlyArray <
99+ JoreStopRegistryTransportModeType | AllOptionEnum
100+ > ;
101+
76102 const isSelected = ( mode : JoreStopRegistryTransportModeType ) =>
77- value . includes ( AllOptionEnum . All ) || value . includes ( mode ) ;
103+ typedValue . includes ( AllOptionEnum . All ) || typedValue . includes ( mode ) ;
78104
79105 const onToggle = ( mode : JoreStopRegistryTransportModeType ) => {
80106 // All selected → Remove clicked and add others
81- if ( value . includes ( AllOptionEnum . All ) ) {
107+ if ( typedValue . includes ( AllOptionEnum . All ) ) {
82108 return onChange ( without ( options , mode ) ) ;
83109 }
84110
85111 // All not selected, but clicked is selected → remove clicked
86- if ( value . includes ( mode ) ) {
87- return onChange ( without ( value , mode ) ) ;
112+ if ( typedValue . includes ( mode ) ) {
113+ return onChange ( without ( typedValue , mode ) ) ;
88114 }
89115
90116 // Clicked not selected -> Add to selection
91- const newSelection = value . concat ( mode ) ;
117+ const newSelection = typedValue . concat ( mode ) ;
92118
93119 // If All select -> Simplify to meta option [All]
94120 if ( newSelection . length === options . length ) {
@@ -101,14 +127,15 @@ export const TransportationModeFilter: FC<DisableableFilterProps> = ({
101127
102128 return (
103129 < fieldset className = { twMerge ( 'flex flex-col' , className ) } onBlur = { onBlur } >
104- < label > { t ( 'stopRegistrySearch.fieldLabels. transportMode' ) } </ label >
130+ < label > { t ( ` ${ translationPrefix } . transportMode` ) } </ label >
105131 < Row className = { twJoin ( 'gap-1' , s . noIconMargins ) } >
106132 { options . map ( ( mode ) => (
107133 < TransportationModeButton
108134 key = { mode }
109135 mode = { mode }
110136 onToggle = { onToggle }
111137 isSelected = { isSelected }
138+ testIdPrefix = { testIdPrefix }
112139 />
113140 ) ) }
114141 </ Row >
0 commit comments