@mistertemp/libs-front-shared 1.1.1

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.
Files changed (44) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +18 -0
  3. package/index.ts +3 -0
  4. package/package.json +100 -0
  5. package/src/components/ControlledInputDate/ControlledInputDate.tsx +82 -0
  6. package/src/components/ControlledInputDate/__tests__/ControlledInputDate.unit.test.tsx +78 -0
  7. package/src/components/ControlledInputDate/index.ts +1 -0
  8. package/src/components/ControlledInputTime/ControlledInputTime.tsx +70 -0
  9. package/src/components/ControlledInputTime/__tests__/ControlledInputTime.unit.test.tsx +108 -0
  10. package/src/components/ControlledInputTime/index.ts +1 -0
  11. package/src/components/CopyToClipboard/CopyToClipboard.tsx +61 -0
  12. package/src/components/CopyToClipboard/__tests__/CopyToClipboard.unit.test.tsx +54 -0
  13. package/src/components/CopyToClipboard/index.ts +1 -0
  14. package/src/components/DateFilter/DateFilter.module.scss +4 -0
  15. package/src/components/DateFilter/DateFilter.tsx +117 -0
  16. package/src/components/DateFilter/DateFilter.utils.tsx +65 -0
  17. package/src/components/DateFilter/__tests__/DateFilter.unit.test.tsx +39 -0
  18. package/src/components/DateFilter/index.ts +1 -0
  19. package/src/components/EnvironmentFilter/EnvironmentFilter.tsx +74 -0
  20. package/src/components/EnvironmentFilter/__tests__/EnvironmentFilter.unit.test.tsx +99 -0
  21. package/src/components/EnvironmentFilter/index.ts +1 -0
  22. package/src/components/Filter/Filter.tsx +127 -0
  23. package/src/components/Filter/index.ts +1 -0
  24. package/src/components/ProfessionFilter/ProfessionFilter.module.scss +4 -0
  25. package/src/components/ProfessionFilter/ProfessionFilter.tsx +145 -0
  26. package/src/components/ProfessionFilter/__tests__/ProfessionFilter.unit.test.tsx +195 -0
  27. package/src/components/ProfessionFilter/index.ts +1 -0
  28. package/src/components/TextFilter/TextFilter.module.scss +3 -0
  29. package/src/components/TextFilter/TextFilter.tsx +112 -0
  30. package/src/components/TextFilter/__tests__/TextFilter.unit.test.tsx +75 -0
  31. package/src/components/TextFilter/index.ts +1 -0
  32. package/src/components/index.ts +8 -0
  33. package/src/hooks/index.ts +3 -0
  34. package/src/hooks/useBundledTranslation.ts +35 -0
  35. package/src/hooks/useDebouncedSearchInput.ts +36 -0
  36. package/src/hooks/usePrevious.ts +13 -0
  37. package/src/locales/fr/date-picker.json +11 -0
  38. package/src/locales/fr/scc-common-filters.json +22 -0
  39. package/src/locales/it/date-picker.json +11 -0
  40. package/src/locales/it/scc-common-filters.json +22 -0
  41. package/src/mocks/professions.ts +24 -0
  42. package/src/typings/filters.ts +7 -0
  43. package/src/typings/index.ts +1 -0
  44. package/tsconfig.json +25 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,15 @@
1
+ # Changelog
2
+
3
+ ## [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
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * publish ([#450](https://github.com/mistertemp/shared-libs-front/issues/450)) ([caf27e3](https://github.com/mistertemp/shared-libs-front/commit/caf27e3519662eb4a66d55317ba0315b442b9aa2))
9
+
10
+ ## [1.1.0](https://github.com/mistertemp/shared-core-components/compare/@mistertemp/libs-front-shared-v1.0.0...@mistertemp/libs-front-shared@1.1.0) (2025-05-05)
11
+
12
+
13
+ ### Features
14
+
15
+ * deploy everything kaboom ([67c63d4](https://github.com/mistertemp/shared-core-components/commit/67c63d4e680a844966a8aff7d25aab4da0e97574))
package/README.md ADDED
@@ -0,0 +1,18 @@
1
+ # @mistertemp/common-core-components
2
+
3
+ ## 1.3.0
4
+
5
+ ### Components
6
+ - **CopyToClipboard** added
7
+
8
+ ## 1.2.0
9
+ ### Features
10
+ - **ControlledInputDate**: add min and max date handling
11
+
12
+ ## 1.1.0
13
+ ### Components
14
+ - **ControlledInputTime** for compatibility with react-hook-form
15
+
16
+ ## 1.0.0
17
+ ### Components
18
+ - **ControlledInputDate** for compatibility with react-hook-form
package/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from './src/components';
2
+ export * from './src/hooks';
3
+ export * from './src/typings';
package/package.json ADDED
@@ -0,0 +1,100 @@
1
+ {
2
+ "name": "@mistertemp/libs-front-shared",
3
+ "version": "1.1.1",
4
+ "main": "index.ts",
5
+ "license": "UNLICENSED",
6
+ "scripts": {
7
+ "demo": "vite --config ./demo/vite.config.ts",
8
+ "lint": "eslint --cache . --ext .ts --ext .tsx",
9
+ "lint:fix": "eslint . --ext .ts --ext .tsx --fix",
10
+ "lint-style": "stylelint '**/*.scss'",
11
+ "lint-style:fix": "stylelint '**/*.scss' --fix",
12
+ "type-check": "tsc --noEmit",
13
+ "test": "NODE_ENV=test ENV=local jest --config=./.jest/jest.config.json",
14
+ "test:watch": "NODE_ENV=test ENV=test jest --watch --config=./jest.config.json --rootDir=."
15
+ },
16
+ "eslintConfig": {
17
+ "extends": "./.eslintrc"
18
+ },
19
+ "prettier": "./.prettierrc.json",
20
+ "stylelint": {
21
+ "extends": "./.stylelintrc"
22
+ },
23
+ "devDependencies": {
24
+ "@axe-core/react": "4.10.0",
25
+ "@mistertemp/design-system": "12.0.0",
26
+ "@mistertemp/libs-work-environments": "0.8.1",
27
+ "@mistertemp/types-shared": "2.2.0",
28
+ "@testing-library/dom": "10.4.0",
29
+ "@testing-library/jest-dom": "6.6.2",
30
+ "@testing-library/react": "12.1.5",
31
+ "@testing-library/user-event": "14.6.1",
32
+ "@types/jest": "29.5.7",
33
+ "@types/lodash": "4.17.4",
34
+ "@types/react": "17.0.52",
35
+ "@types/react-dom": "17.0.18",
36
+ "@types/react-router-dom": "5.3.3",
37
+ "@types/react-test-renderer": "^18.0.7",
38
+ "@types/react-transition-group": "4.4.10",
39
+ "@typescript-eslint/eslint-plugin": "7.18.0",
40
+ "@typescript-eslint/parser": "7.18.0",
41
+ "@vitejs/plugin-react-swc": "3.9.0",
42
+ "date-fns": "2.30.0",
43
+ "eslint": "8.57.0",
44
+ "eslint-config-prettier": "9.1.0",
45
+ "eslint-plugin-import": "2.31.0",
46
+ "eslint-plugin-jsx-a11y": "6.10.0",
47
+ "eslint-plugin-prettier": "5.2.1",
48
+ "eslint-plugin-react": "7.37.1",
49
+ "eslint-plugin-react-hooks": "4.6.0",
50
+ "eslint-plugin-simple-import-sort": "12.1.0",
51
+ "eslint-plugin-testing-library": "6.5.0",
52
+ "fuse.js": "7.0.0",
53
+ "i18next": "23.15.1",
54
+ "i18next-browser-languagedetector": "8.0.4",
55
+ "i18next-http-backend": "2.6.1",
56
+ "identity-obj-proxy": "3.0.0",
57
+ "jest-environment-jsdom": "29.7.0",
58
+ "lodash": "4.17.21",
59
+ "prettier": "3.4.1",
60
+ "react": "17.0.2",
61
+ "react-dom": "17.0.2",
62
+ "react-hook-form": "7.53.0",
63
+ "react-i18next": "14.1.0",
64
+ "react-router-dom": "6.28.0",
65
+ "react-transition-group": "4.4.5",
66
+ "sass": "1.87.0",
67
+ "stylelint": "16.19.1",
68
+ "stylelint-config-idiomatic-order": "10.0.0",
69
+ "stylelint-config-standard-scss": "14.0.0",
70
+ "stylelint-prettier": "5.0.3",
71
+ "ts-jest": "29.2.3",
72
+ "ts-node": "10.9.1",
73
+ "typescript": "5.7.2",
74
+ "vite": "6.3.4",
75
+ "vite-plugin-environment": "1.1.3",
76
+ "vite-plugin-svgr": "4.3.0"
77
+ },
78
+ "peerDependencies": {
79
+ "@mistertemp/design-system": ">=12.0.0",
80
+ "@mistertemp/libs-work-environments": "^0.8.1",
81
+ "i18next": "^23.5.0",
82
+ "i18next-browser-languagedetector": ">=8.0.0",
83
+ "i18next-http-backend": ">=2.2.1",
84
+ "react": "^17.0.2",
85
+ "react-dom": "^17.0.2",
86
+ "react-i18next": "^14.0.0 || ^15.0.0"
87
+ },
88
+ "browserslist": {
89
+ "production": [
90
+ ">0.2%",
91
+ "not dead",
92
+ "not op_mini all"
93
+ ],
94
+ "development": [
95
+ "last 1 chrome version",
96
+ "last 1 firefox version",
97
+ "last 1 safari version"
98
+ ]
99
+ }
100
+ }
@@ -0,0 +1,82 @@
1
+ import { Input, InputProps } from '@mistertemp/design-system';
2
+ import { format, parse } from 'date-fns';
3
+ import React from 'react';
4
+ import { Controller, FieldPath, FieldValues, UseControllerProps } from 'react-hook-form';
5
+
6
+ const controlledInputDateDefaultFormat = 'dd/MM/yyyy';
7
+
8
+ const toDateInputValue = (formDateValue: string | null | undefined, dateFormat: string) =>
9
+ formDateValue
10
+ ? format(parse(formDateValue, dateFormat, new Date()), controlledInputDateDefaultFormat)
11
+ : '';
12
+
13
+ const toDateFormValue = (valueAsDate: Date | null | undefined, dateFormat: string) =>
14
+ valueAsDate ? format(valueAsDate, dateFormat) : '';
15
+
16
+ export type ControlledInputDateProps<
17
+ TFieldValues extends FieldValues,
18
+ TName extends FieldPath<TFieldValues>,
19
+ > = UseControllerProps<TFieldValues, TName> & {
20
+ inputProps: Omit<InputProps, 'inputType' | 'value'>;
21
+ format?: string;
22
+ minErrorMessage?: string;
23
+ maxErrorMessage?: string;
24
+ };
25
+
26
+ export const ControlledInputDate = <
27
+ TFieldValues extends FieldValues = FieldValues,
28
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
29
+ >({
30
+ inputProps: { onChange: externalOnChange, ...otherInputProps },
31
+ format = controlledInputDateDefaultFormat,
32
+ maxErrorMessage = 'max exceeded',
33
+ minErrorMessage = 'min exceeded',
34
+ ...controllerProps
35
+ }: ControlledInputDateProps<TFieldValues, TName>) => (
36
+ <Controller
37
+ {...controllerProps}
38
+ rules={{
39
+ ...controllerProps.rules,
40
+ max: otherInputProps.max
41
+ ? {
42
+ value: otherInputProps.max,
43
+ message: maxErrorMessage,
44
+ }
45
+ : undefined,
46
+ min: otherInputProps.min
47
+ ? {
48
+ value: otherInputProps.min,
49
+ message: minErrorMessage,
50
+ }
51
+ : undefined,
52
+ }}
53
+ render={({ field: { value, onChange, ...fieldProps }, fieldState: { error } }) => (
54
+ <Input
55
+ {...fieldProps}
56
+ inputType="date"
57
+ value={toDateInputValue(value, format)}
58
+ onChange={(e) => {
59
+ const formValue = toDateFormValue(e.target.valueAsDate, format);
60
+
61
+ onChange(formValue);
62
+
63
+ externalOnChange?.({
64
+ target: {
65
+ name: fieldProps.name,
66
+ type: 'date',
67
+ value: formValue,
68
+ valueAsDate: e.target.valueAsDate,
69
+ },
70
+ });
71
+ }}
72
+ hint={
73
+ error?.type == 'max' || error?.type == 'min'
74
+ ? error.message
75
+ : otherInputProps.hint
76
+ }
77
+ hasError={!!error}
78
+ {...otherInputProps}
79
+ />
80
+ )}
81
+ />
82
+ );
@@ -0,0 +1,78 @@
1
+ /* eslint-disable react-hooks/exhaustive-deps */
2
+ import userEvent from '@testing-library/user-event';
3
+ import React from 'react';
4
+ import { useEffect } from 'react';
5
+ import { useForm } from 'react-hook-form';
6
+
7
+ import { act, fireEvent, render, screen } from '../../../../.jest/reactTestingLibrary.config';
8
+ import { ControlledInputDate } from '../ControlledInputDate';
9
+
10
+ describe('Components => ControlledInputDate', () => {
11
+ const FormComponent = ({
12
+ testId,
13
+ value,
14
+ onChange,
15
+ }: {
16
+ testId: string;
17
+ value?: string;
18
+ onChange?: (value: string) => void;
19
+ }) => {
20
+ const { control, setValue, getValues, watch } = useForm<{ date: string }>({
21
+ defaultValues: { date: '' },
22
+ });
23
+
24
+ useEffect(() => {
25
+ if (value) {
26
+ setValue('date', value);
27
+ }
28
+ }, [value]);
29
+
30
+ useEffect(() => {
31
+ onChange?.(getValues('date'));
32
+ }, [watch('date')]);
33
+
34
+ return (
35
+ <ControlledInputDate
36
+ control={control}
37
+ name="date"
38
+ inputProps={{
39
+ 'data-testid': testId,
40
+ type: 'primary',
41
+ size: 'sm',
42
+ }}
43
+ />
44
+ );
45
+ };
46
+
47
+ it('should render', () => {
48
+ render(<FormComponent testId="test" />);
49
+
50
+ expect(screen.getByTestId('test')).toBeInTheDocument();
51
+ expect(screen.getByTestId('test+input')).toBeInTheDocument();
52
+ });
53
+
54
+ it('should apply value', () => {
55
+ render(<FormComponent testId="test" value="01/01/1999" />);
56
+ expect(screen.getByTestId('test+input')).toBeInTheDocument();
57
+ expect(screen.getByTestId('test+input')).toHaveValue('01/01/1999');
58
+ });
59
+
60
+ it('should handle onChange', async () => {
61
+ const onChange = jest.fn();
62
+
63
+ render(<FormComponent testId="test" onChange={onChange} />);
64
+
65
+ const input = screen.getByTestId('test+input');
66
+
67
+ expect(input).toBeInTheDocument();
68
+
69
+ // eslint-disable-next-line testing-library/no-unnecessary-act
70
+ await act(async () => {
71
+ await userEvent.type(input, '2/2/22');
72
+ fireEvent.focusOut(input);
73
+ });
74
+
75
+ expect(input).toHaveValue('02/02/2022');
76
+ expect(onChange).toHaveBeenCalledWith('02/02/2022');
77
+ });
78
+ });
@@ -0,0 +1 @@
1
+ export * from './ControlledInputDate';
@@ -0,0 +1,70 @@
1
+ import { Input, InputProps } from '@mistertemp/design-system';
2
+ import { DEFAULT_TIME_SEPARATOR } from '@mistertemp/design-system/src/components/design-system/Input/InputTime/InputTime.utils';
3
+ import React from 'react';
4
+ import { Controller, FieldPath, FieldValues, UseControllerProps } from 'react-hook-form';
5
+
6
+ const controlledInputTimeDefaultSeparator = ':';
7
+
8
+ const toTimeInputValue = (
9
+ formTimeValue: string | null | undefined,
10
+ inputSeparator: string,
11
+ dataSeparator: string,
12
+ ) => {
13
+ return formTimeValue?.replace(dataSeparator, inputSeparator) ?? '';
14
+ };
15
+
16
+ const toTimeFormValue = (
17
+ inputValue: string | null | undefined,
18
+ inputSeparator: string,
19
+ dataSeparator: string,
20
+ ) => {
21
+ return inputValue?.replace(inputSeparator, dataSeparator) ?? '';
22
+ };
23
+
24
+ export type ControlledInputTimeProps<
25
+ TFieldValues extends FieldValues,
26
+ TName extends FieldPath<TFieldValues>,
27
+ > = UseControllerProps<TFieldValues, TName> & {
28
+ inputProps: Omit<InputProps, 'inputType' | 'value'>;
29
+ separator?: string;
30
+ };
31
+
32
+ export const ControlledInputTime = <
33
+ TFieldValues extends FieldValues = FieldValues,
34
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
35
+ >({
36
+ inputProps: {
37
+ onChange: externalOnChange,
38
+ timeSeparator = DEFAULT_TIME_SEPARATOR,
39
+ ...otherInputProps
40
+ },
41
+ separator = controlledInputTimeDefaultSeparator,
42
+ ...controllerProps
43
+ }: ControlledInputTimeProps<TFieldValues, TName>) => (
44
+ <Controller
45
+ {...controllerProps}
46
+ render={({ field: { value, onChange, ...fieldProps }, fieldState: { error } }) => (
47
+ <Input
48
+ {...fieldProps}
49
+ inputType="time"
50
+ timeSeparator={timeSeparator}
51
+ value={toTimeInputValue(value, timeSeparator, separator)}
52
+ onChange={(e) => {
53
+ const formValue = toTimeFormValue(e.target.value, timeSeparator, separator);
54
+
55
+ onChange(formValue);
56
+
57
+ externalOnChange?.({
58
+ target: {
59
+ name: fieldProps.name,
60
+ type: 'string',
61
+ value: formValue,
62
+ },
63
+ });
64
+ }}
65
+ hasError={!!error}
66
+ {...otherInputProps}
67
+ />
68
+ )}
69
+ />
70
+ );
@@ -0,0 +1,108 @@
1
+ /* eslint-disable react-hooks/exhaustive-deps */
2
+ import userEvent from '@testing-library/user-event';
3
+ import React from 'react';
4
+ import { useEffect } from 'react';
5
+ import { useForm } from 'react-hook-form';
6
+
7
+ import { act, render, screen } from '../../../../.jest/reactTestingLibrary.config';
8
+ import { ControlledInputTime } from '../ControlledInputTime';
9
+
10
+ describe('Components => ControlledInputTime', () => {
11
+ const FormComponent = ({
12
+ testId,
13
+ value,
14
+ dataSeparator,
15
+ inputSeparator,
16
+ onChange,
17
+ }: {
18
+ testId: string;
19
+ value?: string;
20
+ dataSeparator?: string;
21
+ inputSeparator?: string;
22
+ onChange?: (value: string) => void;
23
+ }) => {
24
+ const { control, setValue, getValues, watch } = useForm<{ time: string }>({
25
+ defaultValues: { time: '' },
26
+ });
27
+
28
+ useEffect(() => {
29
+ if (value) {
30
+ setValue('time', value);
31
+ }
32
+ }, [value]);
33
+
34
+ useEffect(() => {
35
+ onChange?.(getValues('time'));
36
+ }, [watch('time')]);
37
+
38
+ return (
39
+ <ControlledInputTime
40
+ control={control}
41
+ name="time"
42
+ separator={dataSeparator}
43
+ inputProps={{
44
+ 'data-testid': testId,
45
+ type: 'primary',
46
+ size: 'sm',
47
+ timeSeparator: inputSeparator,
48
+ }}
49
+ />
50
+ );
51
+ };
52
+
53
+ it('should render', () => {
54
+ render(<FormComponent testId="test" />);
55
+
56
+ expect(screen.getByTestId('test')).toBeInTheDocument();
57
+ expect(screen.getByTestId('test+input')).toBeInTheDocument();
58
+ });
59
+
60
+ it('should apply value', () => {
61
+ render(<FormComponent testId="test" value="10:10" />);
62
+ expect(screen.getByTestId('test+input')).toBeInTheDocument();
63
+ expect(screen.getByTestId('test+input')).toHaveValue('10:10');
64
+ });
65
+
66
+ it('should handle onChange', async () => {
67
+ const onChange = jest.fn();
68
+
69
+ render(<FormComponent testId="test" onChange={onChange} />);
70
+
71
+ const input = screen.getByTestId('test+input');
72
+
73
+ expect(input).toBeInTheDocument();
74
+
75
+ // eslint-disable-next-line testing-library/no-unnecessary-act
76
+ await act(async () => {
77
+ await userEvent.type(input, '2020');
78
+ });
79
+
80
+ expect(input).toHaveValue('20:20');
81
+ expect(onChange).toHaveBeenCalledWith('20:20');
82
+ });
83
+
84
+ it('should handle different separators between input and data', async () => {
85
+ const onChange = jest.fn();
86
+
87
+ render(
88
+ <FormComponent
89
+ testId="test"
90
+ dataSeparator="LL"
91
+ inputSeparator="-"
92
+ onChange={onChange}
93
+ />,
94
+ );
95
+
96
+ const input = screen.getByTestId('test+input');
97
+
98
+ expect(input).toBeInTheDocument();
99
+
100
+ // eslint-disable-next-line testing-library/no-unnecessary-act
101
+ await act(async () => {
102
+ await userEvent.type(input, '2020');
103
+ });
104
+
105
+ expect(input).toHaveValue('20-20');
106
+ expect(onChange).toHaveBeenCalledWith('20LL20');
107
+ });
108
+ });
@@ -0,0 +1 @@
1
+ export * from './ControlledInputTime';
@@ -0,0 +1,61 @@
1
+ import {
2
+ Button,
3
+ DuplicateSvg,
4
+ SvgIcon,
5
+ ToastContext,
6
+ ToastProps,
7
+ ToastType,
8
+ } from '@mistertemp/design-system';
9
+ import React, { MouseEvent, useContext } from 'react';
10
+
11
+ export type CopyToClipboardProps = {
12
+ value?: string | null;
13
+ className?: string;
14
+ icon?: SvgIcon;
15
+ toast?: string | Omit<ToastProps, 'id'>;
16
+ onCopy?: (event: MouseEvent<Element>, value: string) => void;
17
+ 'data-testid'?: string;
18
+ };
19
+
20
+ export const CopyToClipboard = ({
21
+ value,
22
+ className,
23
+ icon = DuplicateSvg,
24
+ toast,
25
+ onCopy,
26
+ 'data-testid': dataTestId = 'copyToClipboard',
27
+ }: CopyToClipboardProps) => {
28
+ const toastContext = useContext(ToastContext);
29
+
30
+ const showToast = () => {
31
+ if (toastContext && toast) {
32
+ const toastProps =
33
+ typeof toast === 'string'
34
+ ? {
35
+ type: ToastType.SUCCESS,
36
+ title: toast,
37
+ }
38
+ : toast;
39
+
40
+ toastContext.enqueue(toastProps);
41
+ }
42
+ };
43
+
44
+ const copyToClipboard = async (event: MouseEvent<Element>) => {
45
+ if (value) {
46
+ await navigator.clipboard.writeText(value);
47
+ showToast();
48
+ onCopy?.(event, value);
49
+ }
50
+ };
51
+
52
+ return (
53
+ <Button
54
+ className={className}
55
+ onClick={copyToClipboard}
56
+ data-testid={dataTestId}
57
+ size="xsmall"
58
+ leftIcon={icon}
59
+ />
60
+ );
61
+ };
@@ -0,0 +1,54 @@
1
+ import userEvent, { UserEvent } from '@testing-library/user-event';
2
+ import React from 'react';
3
+
4
+ import { render, screen } from '../../../../.jest/reactTestingLibrary.config';
5
+ import { CopyToClipboard } from '../CopyToClipboard';
6
+
7
+ describe('Components => CopyToClipboard', () => {
8
+ let user: UserEvent;
9
+
10
+ beforeEach(async () => {
11
+ // Setup clipboard
12
+ user = userEvent.setup();
13
+ });
14
+
15
+ it('should render', () => {
16
+ render(<CopyToClipboard data-testid="test" />);
17
+
18
+ expect(screen.getByTestId('test')).toBeInTheDocument();
19
+ });
20
+
21
+ it('should handle classNames', () => {
22
+ render(<CopyToClipboard data-testid="test" className="className" />);
23
+
24
+ expect(screen.getByTestId('test')).toHaveClass('className');
25
+ });
26
+
27
+ it('should handle copy to clipboard', async () => {
28
+ const onCopy = jest.fn();
29
+ const valueToCopy = 'hello world!';
30
+
31
+ render(<CopyToClipboard data-testid="test" value={valueToCopy} onCopy={onCopy} />);
32
+
33
+ await user.click(screen.getByTestId('test'));
34
+
35
+ const clipboardValue = await navigator.clipboard.readText();
36
+
37
+ expect(clipboardValue).toEqual(valueToCopy);
38
+ expect(onCopy).toHaveBeenCalledWith(expect.any(Object), valueToCopy);
39
+ });
40
+
41
+ it('should handle missing value', async () => {
42
+ const onCopy = jest.fn();
43
+ const valueToCopy = null;
44
+
45
+ render(<CopyToClipboard data-testid="test" value={valueToCopy} onCopy={onCopy} />);
46
+
47
+ await user.click(screen.getByTestId('test'));
48
+
49
+ const clipboardValue = await navigator.clipboard.readText();
50
+
51
+ expect(clipboardValue).toEqual('');
52
+ expect(onCopy).not.toHaveBeenCalled();
53
+ });
54
+ });
@@ -0,0 +1 @@
1
+ export * from './CopyToClipboard';
@@ -0,0 +1,4 @@
1
+ .dropdown {
2
+ height: fit-content;
3
+ max-height: unset;
4
+ }