@mistertemp/libs-front-shared 1.1.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/README.md +1 -1
- package/index.ts +6 -0
- package/package.json +13 -13
- package/src/components/AccountFilter/AccountFilter.tsx +45 -0
- package/src/components/AccountFilter/__tests__/AccountFilter.unit.test.tsx +70 -0
- package/src/components/AccountFilter/index.ts +1 -0
- package/src/components/ControlledInputDate/ControlledInputDate.tsx +39 -0
- package/src/components/ControlledInputTime/ControlledInputTime.tsx +39 -0
- package/src/components/CopyToClipboard/CopyToClipboard.tsx +20 -0
- package/src/components/DateFilter/DateFilter.tsx +21 -0
- package/src/components/DateFilter/DateFilter.utils.tsx +41 -0
- package/src/components/EnvironmentFilter/EnvironmentFilter.tsx +22 -0
- package/src/components/Filter/Filter.tsx +35 -0
- package/src/components/ProfessionFilter/ProfessionFilter.tsx +27 -0
- package/src/components/TextFilter/TextFilter.tsx +21 -0
- package/src/components/index.ts +6 -0
- package/src/hooks/index.ts +5 -0
- package/src/hooks/useBundledTranslation.ts +19 -0
- package/src/hooks/useDebouncedSearchInput.ts +19 -0
- package/src/hooks/usePrevious.ts +10 -0
- package/src/locales/fr/scc-common-filters.json +3 -0
- package/src/locales/it/scc-common-filters.json +3 -0
- package/src/mocks/professions.ts +8 -0
- package/src/typings/filters.ts +8 -0
- package/src/typings/index.ts +5 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.2.0](https://github.com/mistertemp/shared-libs-front/compare/@mistertemp/libs-front-shared@1.1.2...@mistertemp/libs-front-shared@1.2.0) (2025-07-25)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* **Filters:** add accountFilter ([#580](https://github.com/mistertemp/shared-libs-front/issues/580)) ([1179a39](https://github.com/mistertemp/shared-libs-front/commit/1179a39832cd57ced44dd3f79322ea6b7b72af66))
|
|
9
|
+
|
|
10
|
+
## [1.1.2](https://github.com/mistertemp/shared-libs-front/compare/@mistertemp/libs-front-shared@1.1.1...@mistertemp/libs-front-shared@1.1.2) (2025-05-06)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* type-check match lib ([#460](https://github.com/mistertemp/shared-libs-front/issues/460)) ([7f6ff20](https://github.com/mistertemp/shared-libs-front/commit/7f6ff20ab671b156f9424b775aaf385301ce11dd))
|
|
16
|
+
|
|
3
17
|
## [1.1.1](https://github.com/mistertemp/shared-libs-front/compare/@mistertemp/libs-front-shared@1.1.0...@mistertemp/libs-front-shared@1.1.1) (2025-05-05)
|
|
4
18
|
|
|
5
19
|
|
package/README.md
CHANGED
package/index.ts
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @mistertemp/libs-front-shared
|
|
3
|
+
* @description Main entry point for the shared front-end library
|
|
4
|
+
* Exports all components, hooks, and type definitions
|
|
5
|
+
*/
|
|
6
|
+
|
|
1
7
|
export * from './src/components';
|
|
2
8
|
export * from './src/hooks';
|
|
3
9
|
export * from './src/typings';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mistertemp/libs-front-shared",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"main": "index.ts",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"scripts": {
|
|
@@ -22,9 +22,9 @@
|
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@axe-core/react": "4.10.0",
|
|
25
|
-
"@mistertemp/design-system": "12.
|
|
25
|
+
"@mistertemp/design-system": "12.3.0",
|
|
26
26
|
"@mistertemp/libs-work-environments": "0.8.1",
|
|
27
|
-
"@mistertemp/types-shared": "2.
|
|
27
|
+
"@mistertemp/types-shared": "2.3.0",
|
|
28
28
|
"@testing-library/dom": "10.4.0",
|
|
29
29
|
"@testing-library/jest-dom": "6.6.2",
|
|
30
30
|
"@testing-library/react": "12.1.5",
|
|
@@ -38,39 +38,39 @@
|
|
|
38
38
|
"@types/react-transition-group": "4.4.10",
|
|
39
39
|
"@typescript-eslint/eslint-plugin": "7.18.0",
|
|
40
40
|
"@typescript-eslint/parser": "7.18.0",
|
|
41
|
-
"@vitejs/plugin-react-swc": "3.
|
|
41
|
+
"@vitejs/plugin-react-swc": "3.10.0",
|
|
42
42
|
"date-fns": "2.30.0",
|
|
43
43
|
"eslint": "8.57.0",
|
|
44
44
|
"eslint-config-prettier": "9.1.0",
|
|
45
45
|
"eslint-plugin-import": "2.31.0",
|
|
46
46
|
"eslint-plugin-jsx-a11y": "6.10.0",
|
|
47
|
-
"eslint-plugin-prettier": "5.
|
|
47
|
+
"eslint-plugin-prettier": "5.4.0",
|
|
48
48
|
"eslint-plugin-react": "7.37.1",
|
|
49
49
|
"eslint-plugin-react-hooks": "4.6.0",
|
|
50
50
|
"eslint-plugin-simple-import-sort": "12.1.0",
|
|
51
51
|
"eslint-plugin-testing-library": "6.5.0",
|
|
52
|
-
"fuse.js": "7.
|
|
52
|
+
"fuse.js": "7.1.0",
|
|
53
53
|
"i18next": "23.15.1",
|
|
54
54
|
"i18next-browser-languagedetector": "8.0.4",
|
|
55
55
|
"i18next-http-backend": "2.6.1",
|
|
56
56
|
"identity-obj-proxy": "3.0.0",
|
|
57
57
|
"jest-environment-jsdom": "29.7.0",
|
|
58
58
|
"lodash": "4.17.21",
|
|
59
|
-
"prettier": "3.
|
|
59
|
+
"prettier": "3.5.3",
|
|
60
60
|
"react": "17.0.2",
|
|
61
61
|
"react-dom": "17.0.2",
|
|
62
|
-
"react-hook-form": "7.
|
|
62
|
+
"react-hook-form": "7.57.0",
|
|
63
63
|
"react-i18next": "14.1.0",
|
|
64
|
-
"react-router-dom": "6.
|
|
64
|
+
"react-router-dom": "6.30.0",
|
|
65
65
|
"react-transition-group": "4.4.5",
|
|
66
|
-
"sass": "1.
|
|
67
|
-
"stylelint": "16.
|
|
66
|
+
"sass": "1.89.0",
|
|
67
|
+
"stylelint": "16.20.0",
|
|
68
68
|
"stylelint-config-idiomatic-order": "10.0.0",
|
|
69
69
|
"stylelint-config-standard-scss": "14.0.0",
|
|
70
70
|
"stylelint-prettier": "5.0.3",
|
|
71
|
-
"ts-jest": "29.2
|
|
71
|
+
"ts-jest": "29.3.2",
|
|
72
72
|
"ts-node": "10.9.1",
|
|
73
|
-
"typescript": "5.
|
|
73
|
+
"typescript": "5.8.3",
|
|
74
74
|
"vite": "6.3.4",
|
|
75
75
|
"vite-plugin-environment": "1.1.3",
|
|
76
76
|
"vite-plugin-svgr": "4.3.0"
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import { useBundledTranslation } from '../../hooks';
|
|
4
|
+
import fr from '../../locales/fr/scc-common-filters.json';
|
|
5
|
+
import it from '../../locales/it/scc-common-filters.json';
|
|
6
|
+
import { Filter, FilterDropdownOption, FilterProps } from '../Filter';
|
|
7
|
+
|
|
8
|
+
export type AccountFilterProps = Omit<FilterProps, 'name' | 'options'> & {
|
|
9
|
+
name?: string;
|
|
10
|
+
onInputValueChanged?: (value: string | undefined) => void;
|
|
11
|
+
isLoading?: boolean;
|
|
12
|
+
options: FilterDropdownOption[];
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const AccountFilter = ({
|
|
16
|
+
name = 'account',
|
|
17
|
+
onInputValueChanged,
|
|
18
|
+
isLoading = false,
|
|
19
|
+
options,
|
|
20
|
+
...props
|
|
21
|
+
}: AccountFilterProps) => {
|
|
22
|
+
const { t } = useBundledTranslation('scc-common-filters', { fr, it });
|
|
23
|
+
const [inputValue, setInputValue] = useState('');
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<Filter
|
|
27
|
+
name={name}
|
|
28
|
+
placeholder={props.placeholder}
|
|
29
|
+
options={options}
|
|
30
|
+
listProps={{
|
|
31
|
+
search: {
|
|
32
|
+
value: inputValue,
|
|
33
|
+
isLoading,
|
|
34
|
+
onInputValueChanged: (value) => {
|
|
35
|
+
setInputValue(value ?? '');
|
|
36
|
+
onInputValueChanged?.(value);
|
|
37
|
+
},
|
|
38
|
+
placeholder: t('scc-common-filters:filters.account.placeholder'),
|
|
39
|
+
},
|
|
40
|
+
withSelectedTags: true,
|
|
41
|
+
}}
|
|
42
|
+
{...props}
|
|
43
|
+
/>
|
|
44
|
+
);
|
|
45
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { fireEvent, screen, waitFor } from '@testing-library/react';
|
|
2
|
+
import userEvent from '@testing-library/user-event';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
|
|
5
|
+
import { render } from '../../../../.jest/reactTestingLibrary.config';
|
|
6
|
+
import { AccountFilter } from '../AccountFilter';
|
|
7
|
+
|
|
8
|
+
describe('AccountFilter', () => {
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
jest.clearAllMocks();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('should display all account options', async () => {
|
|
14
|
+
render(
|
|
15
|
+
<AccountFilter
|
|
16
|
+
options={[
|
|
17
|
+
{ id: 'testId', label: 'label' },
|
|
18
|
+
{ id: 'testId2', label: 'label2' },
|
|
19
|
+
]}
|
|
20
|
+
/>,
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
fireEvent.click(screen.getByTestId('filters+account+trigger-button'));
|
|
24
|
+
|
|
25
|
+
expect(screen.getByTestId(`filters+account_option_testId`)).toBeInTheDocument();
|
|
26
|
+
expect(screen.getByTestId(`filters+account_option_testId2`)).toBeInTheDocument();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should select one account', async () => {
|
|
30
|
+
const mock = jest.fn();
|
|
31
|
+
|
|
32
|
+
render(
|
|
33
|
+
<AccountFilter
|
|
34
|
+
onDropdownValueChanged={mock}
|
|
35
|
+
options={[
|
|
36
|
+
{ id: 'testId', label: 'label' },
|
|
37
|
+
{ id: 'testId2', label: 'label2' },
|
|
38
|
+
]}
|
|
39
|
+
/>,
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
fireEvent.click(screen.getByTestId('filters+account+trigger-button'));
|
|
43
|
+
|
|
44
|
+
fireEvent.click(screen.getByTestId(`filters+account_option_testId`));
|
|
45
|
+
|
|
46
|
+
await userEvent.click(document.body);
|
|
47
|
+
|
|
48
|
+
await waitFor(() => {
|
|
49
|
+
expect(mock).toHaveBeenCalledWith(['testId']);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should set placeholder title', async () => {
|
|
54
|
+
const newPlaceholder = 'New placeholder';
|
|
55
|
+
|
|
56
|
+
render(
|
|
57
|
+
<AccountFilter
|
|
58
|
+
placeholder={newPlaceholder}
|
|
59
|
+
options={[
|
|
60
|
+
{ id: 'testId', label: 'label' },
|
|
61
|
+
{ id: 'testId2', label: 'label2' },
|
|
62
|
+
]}
|
|
63
|
+
/>,
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
expect(screen.getByTestId('filters+account+trigger-button-title')).toHaveTextContent(
|
|
67
|
+
newPlaceholder,
|
|
68
|
+
);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './AccountFilter';
|
|
@@ -3,16 +3,44 @@ import { format, parse } from 'date-fns';
|
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import { Controller, FieldPath, FieldValues, UseControllerProps } from 'react-hook-form';
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Default date format used for the date input
|
|
8
|
+
*/
|
|
6
9
|
const controlledInputDateDefaultFormat = 'dd/MM/yyyy';
|
|
7
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Converts a form date value string to the format expected by the date input
|
|
13
|
+
*
|
|
14
|
+
* @param {string | null | undefined} formDateValue - The date value from the form
|
|
15
|
+
* @param {string} dateFormat - The format of the date in the form
|
|
16
|
+
* @returns {string} The formatted date string for the input
|
|
17
|
+
*/
|
|
8
18
|
const toDateInputValue = (formDateValue: string | null | undefined, dateFormat: string) =>
|
|
9
19
|
formDateValue
|
|
10
20
|
? format(parse(formDateValue, dateFormat, new Date()), controlledInputDateDefaultFormat)
|
|
11
21
|
: '';
|
|
12
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Converts a Date object to a formatted string for the form
|
|
25
|
+
*
|
|
26
|
+
* @param {Date | null | undefined} valueAsDate - The date object from the input
|
|
27
|
+
* @param {string} dateFormat - The target format for the form value
|
|
28
|
+
* @returns {string} The formatted date string
|
|
29
|
+
*/
|
|
13
30
|
const toDateFormValue = (valueAsDate: Date | null | undefined, dateFormat: string) =>
|
|
14
31
|
valueAsDate ? format(valueAsDate, dateFormat) : '';
|
|
15
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Props for the ControlledInputDate component
|
|
35
|
+
*
|
|
36
|
+
* @template TFieldValues - Type of the form values
|
|
37
|
+
* @template TName - Type of the field name
|
|
38
|
+
* @typedef {Object} ControlledInputDateProps
|
|
39
|
+
* @property {Omit<InputProps, 'inputType' | 'value'>} inputProps - Props for the underlying Input component
|
|
40
|
+
* @property {string} [format='dd/MM/yyyy'] - Date format to use
|
|
41
|
+
* @property {string} [minErrorMessage='min exceeded'] - Error message for minimum date validation
|
|
42
|
+
* @property {string} [maxErrorMessage='max exceeded'] - Error message for maximum date validation
|
|
43
|
+
*/
|
|
16
44
|
export type ControlledInputDateProps<
|
|
17
45
|
TFieldValues extends FieldValues,
|
|
18
46
|
TName extends FieldPath<TFieldValues>,
|
|
@@ -23,6 +51,17 @@ export type ControlledInputDateProps<
|
|
|
23
51
|
maxErrorMessage?: string;
|
|
24
52
|
};
|
|
25
53
|
|
|
54
|
+
/**
|
|
55
|
+
* A controlled date input component for use with react-hook-form
|
|
56
|
+
*
|
|
57
|
+
* This component provides a date input that integrates with react-hook-form's
|
|
58
|
+
* Controller component, handling the conversion between form values and input values.
|
|
59
|
+
*
|
|
60
|
+
* @template TFieldValues - Type of the form values
|
|
61
|
+
* @template TName - Type of the field name
|
|
62
|
+
* @param {ControlledInputDateProps<TFieldValues, TName>} props - Component props
|
|
63
|
+
* @returns {JSX.Element} Controlled date input component
|
|
64
|
+
*/
|
|
26
65
|
export const ControlledInputDate = <
|
|
27
66
|
TFieldValues extends FieldValues = FieldValues,
|
|
28
67
|
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
|
@@ -3,8 +3,19 @@ import { DEFAULT_TIME_SEPARATOR } from '@mistertemp/design-system/src/components
|
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import { Controller, FieldPath, FieldValues, UseControllerProps } from 'react-hook-form';
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Default separator used for time values in form data
|
|
8
|
+
*/
|
|
6
9
|
const controlledInputTimeDefaultSeparator = ':';
|
|
7
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Converts a form time value to the format expected by the time input
|
|
13
|
+
*
|
|
14
|
+
* @param {string | null | undefined} formTimeValue - The time value from the form
|
|
15
|
+
* @param {string} inputSeparator - The separator used in the input component
|
|
16
|
+
* @param {string} dataSeparator - The separator used in the form data
|
|
17
|
+
* @returns {string} The formatted time string for the input
|
|
18
|
+
*/
|
|
8
19
|
const toTimeInputValue = (
|
|
9
20
|
formTimeValue: string | null | undefined,
|
|
10
21
|
inputSeparator: string,
|
|
@@ -13,6 +24,14 @@ const toTimeInputValue = (
|
|
|
13
24
|
return formTimeValue?.replace(dataSeparator, inputSeparator) ?? '';
|
|
14
25
|
};
|
|
15
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Converts an input time value to the format expected by the form
|
|
29
|
+
*
|
|
30
|
+
* @param {string | null | undefined} inputValue - The time value from the input
|
|
31
|
+
* @param {string} inputSeparator - The separator used in the input component
|
|
32
|
+
* @param {string} dataSeparator - The separator used in the form data
|
|
33
|
+
* @returns {string} The formatted time string for the form
|
|
34
|
+
*/
|
|
16
35
|
const toTimeFormValue = (
|
|
17
36
|
inputValue: string | null | undefined,
|
|
18
37
|
inputSeparator: string,
|
|
@@ -21,6 +40,15 @@ const toTimeFormValue = (
|
|
|
21
40
|
return inputValue?.replace(inputSeparator, dataSeparator) ?? '';
|
|
22
41
|
};
|
|
23
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Props for the ControlledInputTime component
|
|
45
|
+
*
|
|
46
|
+
* @template TFieldValues - Type of the form values
|
|
47
|
+
* @template TName - Type of the field name
|
|
48
|
+
* @typedef {Object} ControlledInputTimeProps
|
|
49
|
+
* @property {Omit<InputProps, 'inputType' | 'value'>} inputProps - Props for the underlying Input component
|
|
50
|
+
* @property {string} [separator=':'] - Separator to use in the form data
|
|
51
|
+
*/
|
|
24
52
|
export type ControlledInputTimeProps<
|
|
25
53
|
TFieldValues extends FieldValues,
|
|
26
54
|
TName extends FieldPath<TFieldValues>,
|
|
@@ -29,6 +57,17 @@ export type ControlledInputTimeProps<
|
|
|
29
57
|
separator?: string;
|
|
30
58
|
};
|
|
31
59
|
|
|
60
|
+
/**
|
|
61
|
+
* A controlled time input component for use with react-hook-form
|
|
62
|
+
*
|
|
63
|
+
* This component provides a time input that integrates with react-hook-form's
|
|
64
|
+
* Controller component, handling the conversion between form values and input values.
|
|
65
|
+
*
|
|
66
|
+
* @template TFieldValues - Type of the form values
|
|
67
|
+
* @template TName - Type of the field name
|
|
68
|
+
* @param {ControlledInputTimeProps<TFieldValues, TName>} props - Component props
|
|
69
|
+
* @returns {JSX.Element} Controlled time input component
|
|
70
|
+
*/
|
|
32
71
|
export const ControlledInputTime = <
|
|
33
72
|
TFieldValues extends FieldValues = FieldValues,
|
|
34
73
|
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
|
@@ -8,6 +8,17 @@ import {
|
|
|
8
8
|
} from '@mistertemp/design-system';
|
|
9
9
|
import React, { MouseEvent, useContext } from 'react';
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Props for the CopyToClipboard component
|
|
13
|
+
*
|
|
14
|
+
* @typedef {Object} CopyToClipboardProps
|
|
15
|
+
* @property {string | null} [value] - The text value to copy to clipboard
|
|
16
|
+
* @property {string} [className] - Additional CSS class name
|
|
17
|
+
* @property {SvgIcon} [icon=DuplicateSvg] - Icon to display on the button
|
|
18
|
+
* @property {string | Omit<ToastProps, 'id'>} [toast] - Toast message or props to show after copying
|
|
19
|
+
* @property {Function} [onCopy] - Callback function triggered after copying
|
|
20
|
+
* @property {string} [data-testid='copyToClipboard'] - Test ID for testing
|
|
21
|
+
*/
|
|
11
22
|
export type CopyToClipboardProps = {
|
|
12
23
|
value?: string | null;
|
|
13
24
|
className?: string;
|
|
@@ -17,6 +28,15 @@ export type CopyToClipboardProps = {
|
|
|
17
28
|
'data-testid'?: string;
|
|
18
29
|
};
|
|
19
30
|
|
|
31
|
+
/**
|
|
32
|
+
* A button component that copies text to the clipboard when clicked
|
|
33
|
+
*
|
|
34
|
+
* This component provides a simple way to add copy-to-clipboard functionality
|
|
35
|
+
* with optional toast notification and callback.
|
|
36
|
+
*
|
|
37
|
+
* @param {CopyToClipboardProps} props - Component props
|
|
38
|
+
* @returns {JSX.Element} Copy to clipboard button
|
|
39
|
+
*/
|
|
20
40
|
export const CopyToClipboard = ({
|
|
21
41
|
value,
|
|
22
42
|
className,
|
|
@@ -22,6 +22,18 @@ import itFilters from '../../locales/it/scc-common-filters.json';
|
|
|
22
22
|
import styles from './DateFilter.module.scss';
|
|
23
23
|
import { deduceMode, defaultDateFormat, getDisplayValue, sanitizeValue } from './DateFilter.utils';
|
|
24
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Props for the DateFilter component
|
|
27
|
+
*
|
|
28
|
+
* @typedef {Object} DateFilterProps
|
|
29
|
+
* @property {string} [name='date'] - Name of the filter
|
|
30
|
+
* @property {string} [format] - Date format to use for display
|
|
31
|
+
* @property {Locale} [locale] - Date-fns locale to use
|
|
32
|
+
* @property {Object} [selectorProps] - Props for the dropdown selector trigger
|
|
33
|
+
* @property {MixedDatePickerSelection} [dropdownValue] - Currently selected date(s)
|
|
34
|
+
* @property {Function} [onDropdownValueChanged] - Callback when date selection changes
|
|
35
|
+
* @property {DatePickerProps['availableModes']} [modes] - Available date picker modes
|
|
36
|
+
*/
|
|
25
37
|
export type DateFilterProps = Omit<
|
|
26
38
|
DropdownProps,
|
|
27
39
|
'trigger' | 'onOpenChange' | 'dropdownValue' | 'onDropdownValueChanged' | 'withCTA' | 'ctaTitle'
|
|
@@ -35,6 +47,15 @@ export type DateFilterProps = Omit<
|
|
|
35
47
|
modes?: DatePickerProps['availableModes'];
|
|
36
48
|
};
|
|
37
49
|
|
|
50
|
+
/**
|
|
51
|
+
* A date filter component with dropdown selection
|
|
52
|
+
*
|
|
53
|
+
* This component provides a date picker in a dropdown format, supporting
|
|
54
|
+
* single date, multiple dates, and date range selection modes.
|
|
55
|
+
*
|
|
56
|
+
* @param {DateFilterProps} props - Component props
|
|
57
|
+
* @returns {JSX.Element} Date filter component
|
|
58
|
+
*/
|
|
38
59
|
export const DateFilter: VFC<DateFilterProps> = ({
|
|
39
60
|
name = 'date',
|
|
40
61
|
'data-testid': dataTestId = `filters+${name}`,
|
|
@@ -6,16 +6,44 @@ import {
|
|
|
6
6
|
import { format, isDate as isDateFn, min } from 'date-fns';
|
|
7
7
|
import { DateRange } from 'react-day-picker';
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Default date format used for displaying dates
|
|
11
|
+
*/
|
|
9
12
|
export const defaultDateFormat = 'dd/MM/yy';
|
|
10
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Type guard to check if a selection is an array of dates
|
|
16
|
+
*
|
|
17
|
+
* @param {MixedDatePickerSelection} selection - The date selection to check
|
|
18
|
+
* @returns {boolean} True if selection is an array of dates
|
|
19
|
+
*/
|
|
11
20
|
const isMultiple = (selection: MixedDatePickerSelection): selection is Date[] =>
|
|
12
21
|
Array.isArray(selection);
|
|
13
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Type guard to check if a selection is a date range
|
|
25
|
+
*
|
|
26
|
+
* @param {MixedDatePickerSelection} selection - The date selection to check
|
|
27
|
+
* @returns {boolean} True if selection is a date range
|
|
28
|
+
*/
|
|
14
29
|
const isRange = (selection: MixedDatePickerSelection): selection is DateRange =>
|
|
15
30
|
typeof selection === 'object' && 'from' in selection;
|
|
16
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Type guard to check if a selection is a single date
|
|
34
|
+
*
|
|
35
|
+
* @param {MixedDatePickerSelection} selection - The date selection to check
|
|
36
|
+
* @returns {boolean} True if selection is a single date
|
|
37
|
+
*/
|
|
17
38
|
const isDate = (selection: MixedDatePickerSelection): selection is Date => isDateFn(selection);
|
|
18
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Determines the appropriate date picker mode based on the selection type
|
|
42
|
+
*
|
|
43
|
+
* @param {MixedDatePickerSelection} selection - The current date selection
|
|
44
|
+
* @param {DatePickerProps['availableModes']} availableModes - Available picker modes
|
|
45
|
+
* @returns {DatePickerMode} The appropriate date picker mode
|
|
46
|
+
*/
|
|
19
47
|
export const deduceMode = (
|
|
20
48
|
selection: MixedDatePickerSelection,
|
|
21
49
|
availableModes: DatePickerProps['availableModes'],
|
|
@@ -32,6 +60,12 @@ export const deduceMode = (
|
|
|
32
60
|
}
|
|
33
61
|
};
|
|
34
62
|
|
|
63
|
+
/**
|
|
64
|
+
* Sanitizes a date selection value, handling empty objects
|
|
65
|
+
*
|
|
66
|
+
* @param {MixedDatePickerSelection | object} value - The value to sanitize
|
|
67
|
+
* @returns {MixedDatePickerSelection} The sanitized value
|
|
68
|
+
*/
|
|
35
69
|
export const sanitizeValue = (
|
|
36
70
|
value: MixedDatePickerSelection | object,
|
|
37
71
|
): MixedDatePickerSelection => {
|
|
@@ -46,6 +80,13 @@ export const sanitizeValue = (
|
|
|
46
80
|
return value as MixedDatePickerSelection;
|
|
47
81
|
};
|
|
48
82
|
|
|
83
|
+
/**
|
|
84
|
+
* Generates a display string for the current date selection
|
|
85
|
+
*
|
|
86
|
+
* @param {MixedDatePickerSelection} selection - The date selection
|
|
87
|
+
* @param {string} dateFormat - The format to use for displaying dates
|
|
88
|
+
* @returns {string | undefined} The formatted display string or undefined if no selection
|
|
89
|
+
*/
|
|
49
90
|
export const getDisplayValue = (
|
|
50
91
|
selection: MixedDatePickerSelection,
|
|
51
92
|
dateFormat: string,
|
|
@@ -9,6 +9,9 @@ import fr from '../../locales/fr/scc-common-filters.json';
|
|
|
9
9
|
import it from '../../locales/it/scc-common-filters.json';
|
|
10
10
|
import { Filter, FilterDropdownOption, FilterProps } from '../Filter';
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Default fuzzy search options for environment filtering
|
|
14
|
+
*/
|
|
12
15
|
const DEFAULT_FUSE_OPTIONS: IFuseOptions<FilterDropdownOption<ENVIRONMENTS_CODE>> = {
|
|
13
16
|
isCaseSensitive: false,
|
|
14
17
|
shouldSort: true,
|
|
@@ -18,16 +21,35 @@ const DEFAULT_FUSE_OPTIONS: IFuseOptions<FilterDropdownOption<ENVIRONMENTS_CODE>
|
|
|
18
21
|
threshold: 0.2,
|
|
19
22
|
};
|
|
20
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Props for the EnvironmentFilter component
|
|
26
|
+
*
|
|
27
|
+
* @typedef {Object} EnvironmentFilterProps
|
|
28
|
+
* @property {string} [name='environment'] - Name of the filter
|
|
29
|
+
* @property {IFuseOptions<FilterDropdownOption<ENVIRONMENTS_CODE>>} [fuseOptions] - Custom fuzzy search options
|
|
30
|
+
*/
|
|
21
31
|
export type EnvironmentFilterProps = Omit<FilterProps<ENVIRONMENTS_CODE>, 'name' | 'options'> & {
|
|
22
32
|
name?: string;
|
|
23
33
|
fuseOptions?: IFuseOptions<FilterDropdownOption<ENVIRONMENTS_CODE>>;
|
|
24
34
|
};
|
|
25
35
|
|
|
36
|
+
/**
|
|
37
|
+
* List of all available environment options
|
|
38
|
+
*/
|
|
26
39
|
const options = getAllFlattenedEnvironments().map((env) => ({
|
|
27
40
|
id: env.code,
|
|
28
41
|
label: env.label,
|
|
29
42
|
}));
|
|
30
43
|
|
|
44
|
+
/**
|
|
45
|
+
* A filter component for selecting work environments
|
|
46
|
+
*
|
|
47
|
+
* This component provides a searchable dropdown for selecting one or more
|
|
48
|
+
* work environments from a predefined list.
|
|
49
|
+
*
|
|
50
|
+
* @param {EnvironmentFilterProps} props - Component props
|
|
51
|
+
* @returns {JSX.Element} Environment filter component
|
|
52
|
+
*/
|
|
31
53
|
export const EnvironmentFilter = ({
|
|
32
54
|
listProps,
|
|
33
55
|
name = 'environment',
|
|
@@ -16,12 +16,37 @@ import { useBundledTranslation } from '../../hooks';
|
|
|
16
16
|
import fr from '../../locales/fr/scc-common-filters.json';
|
|
17
17
|
import it from '../../locales/it/scc-common-filters.json';
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Option type for filter dropdown items
|
|
21
|
+
*
|
|
22
|
+
* @template T - Type of the option ID
|
|
23
|
+
* @typedef {Object} FilterDropdownOption
|
|
24
|
+
* @property {T} id - Unique identifier for the option
|
|
25
|
+
* @property {string} label - Display label for the option
|
|
26
|
+
* @property {boolean} [disabled] - Whether the option is disabled
|
|
27
|
+
*/
|
|
19
28
|
export type FilterDropdownOption<T extends string = string> = {
|
|
20
29
|
id: T;
|
|
21
30
|
label: string;
|
|
22
31
|
disabled?: boolean;
|
|
23
32
|
};
|
|
24
33
|
|
|
34
|
+
/**
|
|
35
|
+
* Props for the Filter component
|
|
36
|
+
*
|
|
37
|
+
* @template T - Type of the option ID
|
|
38
|
+
* @typedef {Object} FilterProps
|
|
39
|
+
* @property {Object} [listProps] - Props for the dropdown list provider
|
|
40
|
+
* @property {Object} [selectorProps] - Props for the dropdown selector trigger
|
|
41
|
+
* @property {string} name - Name of the filter
|
|
42
|
+
* @property {string} [data-testid] - Test ID for testing
|
|
43
|
+
* @property {string} [placeholder] - Placeholder text when no selection
|
|
44
|
+
* @property {Array<FilterDropdownOption<T>>} options - Available filter options
|
|
45
|
+
* @property {'single' | 'multiple'} [mode='single'] - Selection mode
|
|
46
|
+
* @property {T[]} [dropdownValue] - Currently selected values
|
|
47
|
+
* @property {Function} [onDropdownValueChanged] - Callback when selection changes
|
|
48
|
+
* @property {string} [ctaTitle] - Title for the CTA button
|
|
49
|
+
*/
|
|
25
50
|
export type FilterProps<T extends string = string> = Omit<
|
|
26
51
|
DropdownProps,
|
|
27
52
|
'trigger' | 'dropdownValue' | 'onDropdownValueChanged' | 'onOpenChange'
|
|
@@ -41,6 +66,16 @@ export type FilterProps<T extends string = string> = Omit<
|
|
|
41
66
|
ctaTitle?: string;
|
|
42
67
|
};
|
|
43
68
|
|
|
69
|
+
/**
|
|
70
|
+
* A generic filter component with dropdown selection
|
|
71
|
+
*
|
|
72
|
+
* This component provides a base for creating dropdown filters with
|
|
73
|
+
* single or multiple selection modes.
|
|
74
|
+
*
|
|
75
|
+
* @template T - Type of the option ID
|
|
76
|
+
* @param {FilterProps<T>} props - Component props
|
|
77
|
+
* @returns {JSX.Element} Filter component
|
|
78
|
+
*/
|
|
44
79
|
export function Filter<T extends string = string>({
|
|
45
80
|
name,
|
|
46
81
|
'data-testid': dataTestId = `filters+${name}`,
|
|
@@ -13,10 +13,28 @@ import { SearchableFilterProps } from '../../typings';
|
|
|
13
13
|
import type { FilterDropdownOption, FilterProps } from '../Filter';
|
|
14
14
|
import styles from './ProfessionFilter.module.scss';
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Props for parent components that use ProfessionFilter
|
|
18
|
+
*
|
|
19
|
+
* @typedef {Omit<FilterProps, 'name' | 'options'>} ProfessionFilterParentProps
|
|
20
|
+
*/
|
|
16
21
|
export type ProfessionFilterParentProps = Omit<FilterProps, 'name' | 'options'>;
|
|
17
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Type alias for profession filter dropdown options
|
|
25
|
+
*
|
|
26
|
+
* @typedef {FilterDropdownOption} ProfessionFilterDropdownOption
|
|
27
|
+
*/
|
|
18
28
|
export type ProfessionFilterDropdownOption = FilterDropdownOption;
|
|
19
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Props for the ProfessionFilter component
|
|
32
|
+
*
|
|
33
|
+
* @typedef {Object} ProfessionFilterProps
|
|
34
|
+
* @property {string} [placeholder] - Placeholder text when no selection
|
|
35
|
+
* @property {FilterDropdownOption[]} options - Available profession options
|
|
36
|
+
* @property {Function} [onChangeSelectedOptions] - Callback when selection changes
|
|
37
|
+
*/
|
|
20
38
|
type ProfessionFilterProps = Omit<ProfessionFilterParentProps, 'onDropdownValueChanged'> &
|
|
21
39
|
SearchableFilterProps & {
|
|
22
40
|
placeholder?: string;
|
|
@@ -24,6 +42,15 @@ type ProfessionFilterProps = Omit<ProfessionFilterParentProps, 'onDropdownValueC
|
|
|
24
42
|
onChangeSelectedOptions?: (options: FilterDropdownOption[]) => void;
|
|
25
43
|
};
|
|
26
44
|
|
|
45
|
+
/**
|
|
46
|
+
* A filter component for selecting professions/qualifications
|
|
47
|
+
*
|
|
48
|
+
* This component provides a searchable dropdown for selecting one or more
|
|
49
|
+
* professions from a provided list, with support for debounced search.
|
|
50
|
+
*
|
|
51
|
+
* @param {ProfessionFilterProps} props - Component props
|
|
52
|
+
* @returns {JSX.Element} Profession filter component
|
|
53
|
+
*/
|
|
27
54
|
export const ProfessionFilter = ({
|
|
28
55
|
dropdownValue,
|
|
29
56
|
onDropdownCTAClick,
|
|
@@ -15,6 +15,18 @@ import fr from '../../locales/fr/scc-common-filters.json';
|
|
|
15
15
|
import it from '../../locales/it/scc-common-filters.json';
|
|
16
16
|
import styles from './TextFilter.module.scss';
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Props for the TextFilter component
|
|
20
|
+
*
|
|
21
|
+
* @typedef {Object} TextFilterProps
|
|
22
|
+
* @property {string} name - Name of the filter
|
|
23
|
+
* @property {'small' | 'medium'} [size='small'] - Size of the filter
|
|
24
|
+
* @property {string} placeholder - Placeholder text when no value
|
|
25
|
+
* @property {string} [dropdownValue] - Current filter value
|
|
26
|
+
* @property {Function} [onDropdownValueChanged] - Callback when value changes
|
|
27
|
+
* @property {Object} [selectorProps] - Props for the dropdown selector trigger
|
|
28
|
+
* @property {Object} [inputProps] - Props for the input component
|
|
29
|
+
*/
|
|
18
30
|
export type TextFilterProps = Omit<
|
|
19
31
|
DropdownProps<Record<string, string>>,
|
|
20
32
|
'id' | 'dropdownValue' | 'onDropdownValueChanged' | 'trigger' | 'withCTA' | 'ctaTitle' | 'size'
|
|
@@ -41,6 +53,15 @@ export type TextFilterProps = Omit<
|
|
|
41
53
|
>;
|
|
42
54
|
};
|
|
43
55
|
|
|
56
|
+
/**
|
|
57
|
+
* A text input filter component with dropdown
|
|
58
|
+
*
|
|
59
|
+
* This component provides a text input in a dropdown format,
|
|
60
|
+
* allowing users to filter by entering text.
|
|
61
|
+
*
|
|
62
|
+
* @param {TextFilterProps} props - Component props
|
|
63
|
+
* @returns {JSX.Element} Text filter component
|
|
64
|
+
*/
|
|
44
65
|
export const TextFilter: VFC<TextFilterProps> = ({
|
|
45
66
|
name,
|
|
46
67
|
placeholder,
|
package/src/components/index.ts
CHANGED
package/src/hooks/index.ts
CHANGED
|
@@ -2,11 +2,30 @@ import type { KeyPrefix, Namespace, TFunction } from 'i18next';
|
|
|
2
2
|
import { useEffect, useState } from 'react';
|
|
3
3
|
import { useTranslation } from 'react-i18next';
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Response type for the useBundledTranslation hook
|
|
7
|
+
*
|
|
8
|
+
* @template N - Namespace type
|
|
9
|
+
* @template TKPrefix - Key prefix type
|
|
10
|
+
* @typedef {Object} UseBundedTranslationResponse
|
|
11
|
+
* @property {TFunction<N, TKPrefix> | Function} t - Translation function
|
|
12
|
+
* @property {boolean} isReady - Whether translations are ready to use
|
|
13
|
+
*/
|
|
5
14
|
export type UseBundedTranslationResponse<N extends Namespace, TKPrefix> = {
|
|
6
15
|
t: TFunction<N, TKPrefix> | ((key: string) => string);
|
|
7
16
|
isReady: boolean;
|
|
8
17
|
};
|
|
9
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Hook for handling bundled translations with i18next
|
|
21
|
+
* Allows loading translations from a provided object rather than fetching them
|
|
22
|
+
*
|
|
23
|
+
* @template N - Namespace type
|
|
24
|
+
* @template TKPrefix - Key prefix type
|
|
25
|
+
* @param {N | Readonly<N>} namespace - The translation namespace
|
|
26
|
+
* @param {Record<string, unknown>} translations - Object containing translations for different languages
|
|
27
|
+
* @returns {UseBundedTranslationResponse<N, TKPrefix>} Translation function and ready state
|
|
28
|
+
*/
|
|
10
29
|
export const useBundledTranslation = <
|
|
11
30
|
N extends Namespace,
|
|
12
31
|
TKPrefix extends KeyPrefix<N> = undefined,
|
|
@@ -3,12 +3,31 @@ import { useMemo } from 'react';
|
|
|
3
3
|
|
|
4
4
|
import { usePrevious } from './usePrevious';
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Configuration options for debounced search input
|
|
8
|
+
*
|
|
9
|
+
* @typedef {Object} DebouncedSearchConfig
|
|
10
|
+
* @property {number} [debounceDelay=100] - Delay in milliseconds before search is triggered
|
|
11
|
+
* @property {boolean} [forceEmptyValue] - Whether to force empty value when input is below threshold
|
|
12
|
+
* @property {number} [inputThreshold=2] - Minimum number of characters required to trigger search
|
|
13
|
+
*/
|
|
6
14
|
export type DebouncedSearchConfig = {
|
|
7
15
|
debounceDelay?: number;
|
|
8
16
|
forceEmptyValue?: boolean;
|
|
9
17
|
inputThreshold?: number;
|
|
10
18
|
};
|
|
11
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Hook for handling debounced search input with threshold control
|
|
22
|
+
*
|
|
23
|
+
* This hook provides a debounced search value that only updates when:
|
|
24
|
+
* 1. The input exceeds the threshold length
|
|
25
|
+
* 2. The input falls below threshold and forceEmptyValue is true
|
|
26
|
+
*
|
|
27
|
+
* @param {string} inputValue - The current input value
|
|
28
|
+
* @param {DebouncedSearchConfig} [config] - Configuration options
|
|
29
|
+
* @returns {string} The debounced search value
|
|
30
|
+
*/
|
|
12
31
|
export function useDebouncedSearchInput(
|
|
13
32
|
inputValue: string,
|
|
14
33
|
config?: DebouncedSearchConfig,
|
package/src/hooks/usePrevious.ts
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import { useRef } from 'react';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Hook to keep track of the previous value of a variable
|
|
5
|
+
*
|
|
6
|
+
* This hook stores the previous value of a variable across renders,
|
|
7
|
+
* only updating when the value actually changes.
|
|
8
|
+
*
|
|
9
|
+
* @template T - Type of the value to track
|
|
10
|
+
* @param {T} value - The current value
|
|
11
|
+
* @returns {T | undefined} The previous value, or undefined on first render
|
|
12
|
+
*/
|
|
3
13
|
export const usePrevious = <T>(value: T) => {
|
|
4
14
|
const currentRef = useRef<T>(value);
|
|
5
15
|
const previousRef = useRef<T>();
|
package/src/mocks/professions.ts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import { FilterDropdownOption } from '../components';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Mock data for profession filter options
|
|
5
|
+
*
|
|
6
|
+
* This array contains sample profession data for use in tests and demos.
|
|
7
|
+
* Each profession has a unique ID and display label.
|
|
8
|
+
*
|
|
9
|
+
* @type {FilterDropdownOption[]}
|
|
10
|
+
*/
|
|
3
11
|
export const filterOptionsMock: FilterDropdownOption[] = [
|
|
4
12
|
{ id: '00270025-6f9e-4e98-83da-4e7c9409653a', label: 'Webmaster' },
|
|
5
13
|
{ id: '21960b88-c606-4daa-b100-1fbc88834f7d', label: 'Carreleur' },
|
package/src/typings/filters.ts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import { DebouncedSearchConfig } from '../hooks';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Props for components that support searchable filtering
|
|
5
|
+
*
|
|
6
|
+
* @typedef {Object} SearchableFilterProps
|
|
7
|
+
* @property {boolean} [isLoading] - Indicates if search results are loading
|
|
8
|
+
* @property {Function} [onChangeSearchText] - Callback triggered when search text changes
|
|
9
|
+
* @property {DebouncedSearchConfig} searchConfig - Configuration for debounced search behavior
|
|
10
|
+
*/
|
|
3
11
|
export type SearchableFilterProps = {
|
|
4
12
|
isLoading?: boolean;
|
|
5
13
|
onChangeSearchText?: (text: string) => void;
|