jamespot-react-components 1.0.2 → 1.0.16

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 (91) hide show
  1. package/.storybook/StorybookContainer/StorybookContainer.css +1 -0
  2. package/babel.config.js +0 -1
  3. package/build/jamespot-react-components.js +254 -235
  4. package/build/jamespot-react-components.js.LICENSE.txt +19 -13
  5. package/build/jamespot-react-components.js.map +1 -1
  6. package/build/src/components/Common/JRCConditionalWrapper.d.ts +4 -4
  7. package/build/src/components/Form/Input/JRCInputCheckbox/JRCInputCheckbox.d.ts +5 -2
  8. package/build/src/components/Form/Input/JRCInputDate/JRCInputDate.d.ts +11 -0
  9. package/build/src/components/Form/Input/JRCInputDate/JRCInputDate.stories.d.ts +5 -0
  10. package/build/src/components/Form/Input/JRCInputText/JRCInputText.d.ts +1 -1
  11. package/build/src/components/Form/Input/JRCInputTextarea/JRCInputTextarea.d.ts +15 -0
  12. package/build/src/components/Form/Input/JRCInputTextarea/JRCInputTextarea.stories.d.ts +5 -0
  13. package/build/src/components/JRCAppContainer/JRCAppContainer.d.ts +1 -0
  14. package/build/src/components/JRCButton/JRCButton.d.ts +25 -0
  15. package/build/src/components/JRCButton/JRCButtonConfig.d.ts +1 -2
  16. package/build/src/components/JRCEllipsis/JRCEllipsis.d.ts +5 -0
  17. package/build/src/components/JRCEllipsis/JRCEllipsis.stories.d.ts +5 -0
  18. package/build/src/components/JRCHref/JRCHref.d.ts +18 -10
  19. package/build/src/components/JRCImg/url.util.d.ts +3 -3
  20. package/build/src/components/JRCList/JRCList.d.ts +2 -0
  21. package/build/src/components/JRCModal/JRCModal.d.ts +5 -1
  22. package/build/src/components/JRCModal/JRCModal.styles.d.ts +0 -1
  23. package/build/src/components/JRCTag/JRCTag.d.ts +2 -0
  24. package/build/src/components/Templates/JRCBase.template.d.ts +9 -0
  25. package/build/src/components/Templates/JRCTemplate.stories.d.ts +13 -0
  26. package/build/src/components/Templates/JRCTwoColumns.template.d.ts +7 -0
  27. package/build/src/hooks/UseDidMountEffect.d.ts +1 -2
  28. package/build/src/index.d.ts +11 -8
  29. package/build/src/styles/theme.d.ts +4 -1
  30. package/build/src/types.d.ts +6 -2
  31. package/externals.d.ts +0 -1
  32. package/externals.json +3 -1
  33. package/package.json +4 -4
  34. package/src/components/Common/JRCConditionalWrapper.tsx +6 -13
  35. package/src/components/Form/Input/JRCFormColor/JRCFormColor.tsx +12 -12
  36. package/src/components/Form/Input/JRCFormEmail/JRCInputEmail.tsx +4 -8
  37. package/src/components/Form/Input/JRCFormSelect/JRCFormSelect.tsx +5 -1
  38. package/src/components/Form/Input/JRCFormSelect/JRCFormSelectTag.tsx +3 -1
  39. package/src/components/Form/Input/JRCInputCheckbox/JRCInputCheckbox.stories.tsx +1 -4
  40. package/src/components/Form/Input/JRCInputCheckbox/JRCInputCheckbox.tsx +12 -10
  41. package/src/components/Form/Input/JRCInputDate/JRCInputDate.stories.tsx +50 -0
  42. package/src/components/Form/Input/JRCInputDate/JRCInputDate.tsx +26 -0
  43. package/src/components/Form/Input/JRCInputText/JRCInputText.tsx +2 -2
  44. package/src/components/Form/Input/JRCInputTextarea/JRCInputTextarea.stories.tsx +52 -0
  45. package/src/components/Form/Input/JRCInputTextarea/JRCInputTextarea.tsx +36 -0
  46. package/src/components/Form/Input/JRCSelect/JRCInputSelect.tsx +1 -1
  47. package/src/components/JRCAppContainer/JRCAppContainer.tsx +6 -2
  48. package/src/components/JRCAppLeftColumn/JRCAppLeftColumn.styles.tsx +2 -1
  49. package/src/components/JRCAvatar/JRCAvatar.test.tsx +10 -6
  50. package/src/components/JRCAvatar/JRCAvatar.tsx +1 -0
  51. package/src/components/JRCButton/JRCButton.stories.tsx +1 -1
  52. package/src/components/JRCButton/JRCButton.tsx +9 -3
  53. package/src/components/JRCButton/JRCButtonConfig.tsx +1 -1
  54. package/src/components/JRCButton/JRCValidationButton.tsx +10 -4
  55. package/src/components/JRCButton/__snapshots__/JRCButton.test.tsx.snap +1 -2
  56. package/src/components/JRCButtonDropdown/JRCButtonDropdown.tsx +2 -2
  57. package/src/components/JRCEllipsis/JRCEllipsis.stories.tsx +18 -0
  58. package/src/components/JRCEllipsis/JRCEllipsis.tsx +22 -0
  59. package/src/components/JRCHref/JRCHref.stories.tsx +2 -0
  60. package/src/components/JRCHref/JRCHref.tsx +42 -15
  61. package/src/components/JRCIcon/JRCIcon.tsx +1 -1
  62. package/src/components/JRCIconButton/JRCIconButton.tsx +1 -4
  63. package/src/components/JRCImg/JRCImg.tsx +4 -2
  64. package/src/components/JRCImg/url.util.ts +7 -6
  65. package/src/components/JRCImg/url.utils.test.ts +7 -1
  66. package/src/components/JRCList/JRCList.styles.tsx +16 -2
  67. package/src/components/JRCList/JRCList.tsx +5 -5
  68. package/src/components/JRCList/JRCListMockData.stories.tsx +1 -1
  69. package/src/components/JRCModal/JRCModal.styles.tsx +0 -6
  70. package/src/components/JRCModal/JRCModal.tsx +75 -61
  71. package/src/components/JRCTag/JRCTag.tsx +29 -9
  72. package/src/components/JRCThemeProvider/animation.css +19 -0
  73. package/src/components/JRCThemeProvider/font.css +1 -1
  74. package/src/components/JRCThemeProvider/gabarit.css +4 -0
  75. package/src/components/JRCTooltip/JRCTooltip.tsx +5 -2
  76. package/src/components/JRCTypography/__snapshots__/JRCTypography.test.tsx.snap +1 -1
  77. package/src/components/Templates/JRCBase.template.tsx +39 -0
  78. package/src/components/Templates/JRCTemplate.stories.tsx +55 -0
  79. package/src/components/Templates/JRCTwoColumns.template.tsx +20 -0
  80. package/src/hooks/UseDidMountEffect.tsx +1 -3
  81. package/src/index.tsx +17 -10
  82. package/src/styles/theme.tsx +12 -6
  83. package/src/translation/lang.json +3 -2
  84. package/src/types.ts +8 -2
  85. package/build/src/components/Form/Input/JRCFormTextEditor/JRCFormTextEditor.d.ts +0 -10
  86. package/build/src/components/Form/Input/JRCFormTextEditor/JRCFormTextEditor.stories.d.ts +0 -5
  87. package/build/src/components/JRCColumnCenterTitle/JRCColumnCenterTitle.d.ts +0 -12
  88. package/src/components/Form/Input/JRCFormTextEditor/JRCFormTextEditor.stories.tsx +0 -30
  89. package/src/components/Form/Input/JRCFormTextEditor/JRCFormTextEditor.tsx +0 -36
  90. package/src/components/JRCColumnCenterTitle/JRCColumnCenterTitle.tsx +0 -26
  91. package/src/variables.scss +0 -67
@@ -49,6 +49,7 @@ const JRCFormSelectTagRenderer = <T,>(
49
49
  onMouseDown={props.removeProps.onMouseDown}
50
50
  onClick={(e) => props.removeProps.onClick(e)}
51
51
  focusable={true}
52
+ clickVariant="delete"
52
53
  isFocused={props.isFocused}
53
54
  uri={props.data.uri}
54
55
  label={getOptionLabel(props)}
@@ -60,7 +61,7 @@ const JRCFormSelectTagRenderer = <T,>(
60
61
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
61
62
  // @ts-ignore
62
63
  SingleValue: (props: SingleValueProps<OptionTypeBase>) => (
63
- <JRCTag uri={props.data.uri} label={getOptionLabel(props)} />
64
+ <JRCTag uri={props.data.uri} label={getOptionLabel(props)} clickVariant="delete" />
64
65
  ),
65
66
  // FIXME TYPESCRIPT-STRICT
66
67
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
@@ -88,6 +89,7 @@ const Option = <isMulti extends boolean>(props: OptionProps<OptionTypeBase, isMu
88
89
  <JRCTag
89
90
  uri={props.data.uri}
90
91
  focusable={true}
92
+ clickVariant="select"
91
93
  isFocused={props.isFocused}
92
94
  label={getOptionLabel(props)}
93
95
  />
@@ -13,7 +13,7 @@ export default {
13
13
  const Template: Story<JRCInputCheckboxProps<any>> = (args) => {
14
14
  const { handleSubmit, control } = useForm({
15
15
  defaultValues: {
16
- [args.name]: [],
16
+ checkbox: [],
17
17
  },
18
18
  criteriaMode: 'all',
19
19
  });
@@ -23,9 +23,6 @@ const Template: Story<JRCInputCheckboxProps<any>> = (args) => {
23
23
  };
24
24
 
25
25
  return (
26
- // FIXME TYPESCRIPT-STRICT
27
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
28
- // @ts-ignore
29
26
  <form onSubmit={handleSubmit(onSubmit)}>
30
27
  <JRCInputCheckbox {...args} control={control as any} />
31
28
  <input type="submit" />
@@ -12,12 +12,11 @@ import { deepEqual } from '../../../../utils/utils.object';
12
12
  import { DataCy } from '../../../../types/dataAttributes';
13
13
  import { JRCInputFieldProps } from '../Common/JRCFormFieldRenderer.types';
14
14
  import { CheckboxValue } from './JRCInputCheckbox.types';
15
-
16
- type Value<T> = string | number | T;
15
+ import { RefCallBack } from 'react-hook-form';
17
16
 
18
17
  type CheckboxOption<TFieldValues> = {
19
18
  label: string | JSX.Element;
20
- value: Value<TFieldValues>;
19
+ value: string | number | TFieldValues;
21
20
  disabled?: boolean;
22
21
  };
23
22
 
@@ -35,18 +34,19 @@ export type CheckboxProps<TFieldValues> = DataCy & {
35
34
  };
36
35
 
37
36
  export type JRCInputCheckboxProps<TFieldValues> = CheckboxProps<TFieldValues> & JRCInputFieldProps<TFieldValues>;
37
+ export type NativeCheckboxProps<TFieldValues> = CheckboxProps<TFieldValues> &
38
+ Omit<ControllerRenderProps<TFieldValues>, 'value'> & { value: TFieldValues | TFieldValues[] };
38
39
 
39
- function RenderInput<TFieldValues>({
40
- variant = 'default',
41
- ...props
42
- }: CheckboxProps<TFieldValues> &
43
- Omit<ControllerRenderProps<TFieldValues>, 'value'> & { value: TFieldValues | TFieldValues[] }) {
40
+ const RenderInput = React.forwardRef(function <TFieldValues>(
41
+ { variant = 'default', ...props }: NativeCheckboxProps<TFieldValues>,
42
+ ref: React.ForwardedRef<HTMLInputElement>,
43
+ ) {
44
44
  const field = {
45
45
  onChange: props.onChange,
46
46
  onBlur: props.onBlur,
47
47
  value: props.value,
48
48
  name: props.name,
49
- ref: props.ref,
49
+ ref: ref as RefCallBack,
50
50
  };
51
51
 
52
52
  const [valueTypeIsBoolean, setValueTypeIsBoolean] = React.useState(
@@ -98,6 +98,7 @@ function RenderInput<TFieldValues>({
98
98
  {props.checkboxMode === 'radio' ? (
99
99
  <JRCRadio
100
100
  name={props.name}
101
+ ref={(index === 0 && ref) || undefined}
101
102
  data-cy={props.dataCy && `${props.dataCy}-option-${index}`}
102
103
  disabled={props.disabled || option.disabled}
103
104
  checked={checked}
@@ -109,6 +110,7 @@ function RenderInput<TFieldValues>({
109
110
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
110
111
  // @ts-ignore
111
112
  checkboxMode={props.checkboxMode}
113
+ ref={(index === 0 && ref) || undefined}
112
114
  name={props.name}
113
115
  data-cy={props.dataCy && `${props.dataCy}-option-${index}`}
114
116
  disabled={props.disabled || option.disabled}
@@ -147,7 +149,7 @@ function RenderInput<TFieldValues>({
147
149
  })}
148
150
  </DirectionContainer>
149
151
  );
150
- }
152
+ }) as unknown as <T>(props: NativeCheckboxProps<T>) => JSX.Element;
151
153
 
152
154
  export function JRCInputCheckbox<TFieldValues>({
153
155
  name,
@@ -0,0 +1,50 @@
1
+ import * as React from 'react';
2
+ import { JRCInputDate } from './JRCInputDate';
3
+ import { useForm } from 'react-hook-form';
4
+ import { JRCInputFieldProps } from '../Common/JRCFormFieldRenderer.types';
5
+ import { SubmitHandler } from 'react-hook-form/dist/types/form';
6
+ import { Meta, Story } from '@storybook/react';
7
+
8
+ export default {
9
+ title: 'Inputs/new',
10
+ component: JRCInputDate,
11
+ } as Meta;
12
+
13
+ const Template: Story<JRCInputFieldProps<'date'>> = (args) => {
14
+ const { handleSubmit, control, reset } = useForm({
15
+ defaultValues: {
16
+ date: '',
17
+ },
18
+ criteriaMode: 'all',
19
+ });
20
+
21
+ const onSubmit: SubmitHandler<{ date: string }> = (data) => {
22
+ alert(JSON.stringify(data, null, 2));
23
+ };
24
+
25
+ return (
26
+ <form onSubmit={handleSubmit(onSubmit)}>
27
+ <JRCInputDate {...args} name="date" control={control} />
28
+ <input type="submit" />
29
+ <input type="reset" />
30
+ <button type="button" onClick={() => reset()}>
31
+ Custom Reset
32
+ </button>
33
+ </form>
34
+ );
35
+ };
36
+
37
+ export const InputDate = Template.bind({});
38
+ InputDate.args = {
39
+ label: 'Date de création du groupe',
40
+ description:
41
+ "La date de création du groupe doit permettre en un clin d'oeil de connaitre la date de création du groupe",
42
+ helper: { description: "Ajouter une date cohérente ! L'année 218 n'est pas cohérente", title: 'Indication' },
43
+ rules: {
44
+ min: {
45
+ value: '2023-11-11',
46
+ message: 'La date doit être supérieur au 11/11/2023',
47
+ },
48
+ required: true,
49
+ },
50
+ };
@@ -0,0 +1,26 @@
1
+ import * as React from 'react';
2
+ import { StyledInput } from '../JRCStyledInput';
3
+ import { JRCInputFieldProps } from '../Common/JRCFormFieldRenderer.types';
4
+ import { JRCFormFieldRenderer } from '../Common/JRCFormFieldRenderer';
5
+ import { DataCy } from '../../../../types/dataAttributes';
6
+
7
+ export type JRCInputDateProps = DataCy & React.ComponentPropsWithoutRef<'input'>;
8
+
9
+ const RenderInput = React.forwardRef((props: JRCInputDateProps, ref: React.ForwardedRef<HTMLInputElement>) => {
10
+ return <StyledInput type="date" id={props.name} {...props} ref={ref} />;
11
+ });
12
+
13
+ /**
14
+ * Component used as a <input type="date"/>
15
+ * @param props JRCInputFieldProps
16
+ * validation props: required
17
+ * @returns JSX.Element
18
+ */
19
+ export function JRCInputDate<T>(props: JRCInputFieldProps<T>) {
20
+ return (
21
+ <JRCFormFieldRenderer
22
+ {...props}
23
+ renderFunction={({ value, ...field }) => <RenderInput {...props} {...field} value={value as string} />}
24
+ />
25
+ );
26
+ }
@@ -4,9 +4,9 @@ import { JRCInputFieldProps } from '../Common/JRCFormFieldRenderer.types';
4
4
  import { JRCFormFieldRenderer } from '../Common/JRCFormFieldRenderer';
5
5
  import { DataCy } from '../../../../types/dataAttributes';
6
6
 
7
- export type InputTextProps = DataCy & React.ComponentPropsWithoutRef<'input'>;
7
+ export type JRCInputTextProps = DataCy & React.ComponentPropsWithoutRef<'input'>;
8
8
 
9
- const RenderInput = React.forwardRef((props: InputTextProps, ref: React.ForwardedRef<HTMLInputElement>) => {
9
+ const RenderInput = React.forwardRef((props: JRCInputTextProps, ref: React.ForwardedRef<HTMLInputElement>) => {
10
10
  return <StyledInput type="text" id={props.name} {...props} ref={ref} />;
11
11
  });
12
12
 
@@ -0,0 +1,52 @@
1
+ import * as React from 'react';
2
+ import { JRCInputTextarea } from './JRCInputTextarea';
3
+ import { useForm } from 'react-hook-form';
4
+ import { JRCInputFieldProps } from '../Common/JRCFormFieldRenderer.types';
5
+ import { SubmitHandler } from 'react-hook-form/dist/types/form';
6
+ import { Meta, Story } from '@storybook/react';
7
+
8
+ export default {
9
+ title: 'Inputs/new',
10
+ component: JRCInputTextarea,
11
+ } as Meta;
12
+
13
+ const Template: Story<JRCInputFieldProps<'textarea'>> = (args) => {
14
+ const { handleSubmit, control, reset } = useForm({
15
+ defaultValues: {
16
+ textarea: '',
17
+ },
18
+ criteriaMode: 'all',
19
+ });
20
+
21
+ const onSubmit: SubmitHandler<{ textarea: string }> = (data) => {
22
+ alert(JSON.stringify(data, null, 2));
23
+ };
24
+
25
+ return (
26
+ <form onSubmit={handleSubmit(onSubmit)}>
27
+ <JRCInputTextarea {...args} name="textarea" control={control} />
28
+ <input type="submit" />
29
+ <input type="reset" />
30
+ <button type="button" onClick={() => reset()}>
31
+ Custom Reset
32
+ </button>
33
+ </form>
34
+ );
35
+ };
36
+
37
+ export const InputTextarea = Template.bind({});
38
+ InputTextarea.args = {
39
+ label: 'Nom de groupe',
40
+ description: "Le nom du groupe doit permettre en un clin d'oeil de connaitre le sujet du groupe",
41
+ placeholder: 'Présentations',
42
+ helper: { description: 'Ne faites pas commencer votre groupe par "Groupe de"', title: 'Indication' },
43
+ rules: {
44
+ maxLength: {
45
+ value: 25,
46
+ message:
47
+ 'This is an example error. For instance, the text should not be written with more than 25 characters. This message is not the default one.',
48
+ },
49
+ minLength: 3,
50
+ required: true,
51
+ },
52
+ };
@@ -0,0 +1,36 @@
1
+ import * as React from 'react';
2
+ import { cssStyledInput } from '../JRCStyledInput';
3
+ import { JRCInputFieldProps } from '../Common/JRCFormFieldRenderer.types';
4
+ import { JRCFormFieldRenderer } from '../Common/JRCFormFieldRenderer';
5
+ import { DataCy } from '../../../../types/dataAttributes';
6
+ import styled from 'styled-components';
7
+
8
+ export type JRCInputTextareaProps = DataCy & React.ComponentPropsWithoutRef<'textarea'>;
9
+
10
+ export const StyledTextArea = styled.textarea`
11
+ ${cssStyledInput}
12
+ &&&& {
13
+ padding-top: 10px;
14
+ height: 140px;
15
+ resize: vertical;
16
+ }
17
+ `;
18
+
19
+ const RenderInput = React.forwardRef((props: JRCInputTextareaProps, ref: React.ForwardedRef<HTMLTextAreaElement>) => {
20
+ return <StyledTextArea id={props.name} {...props} ref={ref} />;
21
+ });
22
+
23
+ /**
24
+ * Component used as a <input type="text"/>
25
+ * @param props JRCInputFieldProps
26
+ * validation props: required
27
+ * @returns JSX.Element
28
+ */
29
+ export function JRCInputTextarea<T>(props: JRCInputFieldProps<T>) {
30
+ return (
31
+ <JRCFormFieldRenderer
32
+ {...props}
33
+ renderFunction={({ value, ...field }) => <RenderInput {...props} {...field} value={value as string} />}
34
+ />
35
+ );
36
+ }
@@ -56,7 +56,7 @@ export const Select = React.forwardRef(function <T>(
56
56
  if (menuRef.current && selectedOptionRef) scrollIntoView(menuRef.current, selectedOptionRef);
57
57
  }
58
58
 
59
- // Hack to use .focus() outside of this component while beeing able to access the ref within this component
59
+ // Hack to use .focus() outside of this component while being able to access the ref within this component
60
60
  React.useImperativeHandle(
61
61
  ref,
62
62
  () =>
@@ -1,13 +1,17 @@
1
1
  import * as React from 'react';
2
2
  import styled from 'styled-components';
3
3
 
4
+ export const CONTAINER_PADDING = 32;
5
+
4
6
  const Container = styled.div`
5
7
  display: flex;
6
8
  max-width: 1600px;
7
9
  width: 100%;
8
10
  margin: auto;
9
- gap: 16px;
10
- padding: 16px;
11
+ gap: ${CONTAINER_PADDING}px;
12
+ padding: ${CONTAINER_PADDING}px;
13
+ background-color: ${(props) => props.theme.color.white};
14
+ min-height: calc(var(--body-height) - ${CONTAINER_PADDING * 2}px);
11
15
  `;
12
16
 
13
17
  export interface JRCAppContainerProps {
@@ -2,6 +2,7 @@ import { ItemButton } from '../JRCTabs/JRCTabs';
2
2
  import styled from 'styled-components';
3
3
  import { JRCH2 } from '../JRCTypo/JRCTypo';
4
4
  import { JRCIconButton } from '../JRCIconButton/JRCIconButton';
5
+ import { CONTAINER_PADDING } from 'components/JRCAppContainer/JRCAppContainer';
5
6
 
6
7
  export const JRCAppLeftColumnWrapperTop = styled.div`
7
8
  display: flex;
@@ -21,7 +22,7 @@ export const ColumnRelativeSticky = styled.div<{ isMinimized: boolean; isClosed:
21
22
  display: flex;
22
23
  position: sticky;
23
24
  bottom: 0;
24
- min-height: calc(var(--body-height) - 32px);
25
+ min-height: calc(var(--body-height) - ${CONTAINER_PADDING * 2}px);
25
26
  background: ${(props) => props.theme.color.grey0};
26
27
  min-width: ${(props) => (props.isMinimized ? '80px' : '300px')};
27
28
  overflow: ${(props) => (props.isClosed ? 'visible' : 'hidden')};
@@ -4,19 +4,23 @@ import { screen } from '@testing-library/react';
4
4
 
5
5
  import JRCAvatar from './JRCAvatar';
6
6
 
7
+ const SIZE = 3;
8
+
7
9
  describe('JRCAvatar', () => {
8
10
  beforeEach(() => {
9
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
10
- // @ts-ignore FIXME remove that when upgrading to typescript version > 4.2.x
11
- renderWithWrapper(<JRCAvatar type="user" recordId="1234" size="3" alt="avatar of user John Doe" />);
11
+ renderWithWrapper(
12
+ <JRCAvatar type="user" recordId="1234" size={SIZE} alt="avatar of user John Doe" />,
13
+ );
12
14
  });
13
15
 
14
16
  test('medium avatar to have a proper size', () => {
15
- expect(screen.getByRole('img')).toHaveAttribute('width', '24');
16
- expect(screen.getByRole('img')).toHaveAttribute('height', '24');
17
+ const width = (8 * SIZE).toString();
18
+ expect(screen.getByRole('img')).toHaveAttribute('width', width);
19
+ expect(screen.getByRole('img')).toHaveAttribute('height', width);
17
20
  });
18
21
 
19
22
  test('medium avatar to download an img twice the container size', () => {
20
- expect(screen.getByRole('img')).toHaveAttribute('src', '/imagecache/48x48/user/1234.png');
23
+ const width = (Math.ceil(window.devicePixelRatio) * 8 * SIZE).toString();
24
+ expect(screen.getByRole('img')).toHaveAttribute('src', `/imagecache/${width}x${width}/user/1234.png`);
21
25
  });
22
26
  });
@@ -71,6 +71,7 @@ const JRCAvatar = (props: JRCAvatarProps) => {
71
71
  ...otherProps,
72
72
  width: imgSize,
73
73
  height: imgSize,
74
+ size: `${imgSize * 2}x${imgSize * 2}`,
74
75
  format: format,
75
76
  alt: alt,
76
77
  'aria-hidden': alt ? undefined : true,
@@ -44,7 +44,7 @@ export const AllButtons = () => {
44
44
  </JRCButton>
45
45
  <JRCButton
46
46
  onClick={action('button-click')}
47
- // icon="icon-check"
47
+ icon="icon-check"
48
48
  type="submit"
49
49
  color="primary"
50
50
  variant="contained">
@@ -2,7 +2,7 @@ import * as React from 'react';
2
2
  import JRCIcon, { JRCIconProps } from '../JRCIcon/JRCIcon';
3
3
  import styled from 'styled-components';
4
4
  import JRCLoader from 'components/JRCLoader/JRCLoader';
5
- import config, { ButtonType } from './JRCButtonConfig';
5
+ import { BUTTON_CONFIG, ButtonType } from './JRCButtonConfig';
6
6
  import { ForwardedRef } from 'react';
7
7
  import { DataCy } from '../../types/dataAttributes';
8
8
 
@@ -39,7 +39,10 @@ export const transformColor = (color?: string): JRCIconProps['color'] => {
39
39
  }
40
40
  };
41
41
 
42
- const Button = styled.button<{ themeButton: ButtonType; hasChildren: boolean } & JRCButtonProps>`
42
+ /**
43
+ * Button styling. May be used as a <a> tag
44
+ */
45
+ export const Button = styled.button<{ themeButton: ButtonType; hasChildren: boolean } & JRCButtonProps>`
43
46
  float: ${(props) => props.float};
44
47
  min-width: ${(props) => props.minWidth};
45
48
  display: inline-flex;
@@ -58,7 +61,10 @@ const Button = styled.button<{ themeButton: ButtonType; hasChildren: boolean } &
58
61
  box-sizing: border-box;
59
62
  border: 2px solid ${(props) => props.theme.color[props.themeButton.border]};
60
63
  transition: color 0.5s, background-color 0.5s, border 0.5s;
64
+ text-decoration: none;
61
65
  &:hover {
66
+ text-decoration: none;
67
+ color: ${(props) => props.theme.color[props.themeButton.color]};
62
68
  background-color: ${(props) => props.theme.color[props.themeButton.hover.background]};
63
69
  border-color: ${(props) => props.theme.color[props.themeButton.hover.border]};
64
70
  }
@@ -93,7 +99,7 @@ const JRCButton = React.forwardRef(
93
99
  ref: ForwardedRef<HTMLButtonElement>,
94
100
  ) => {
95
101
  const disabled = props.disabled || props.loader;
96
- const themeButton = config[variant][disabled ? 'disabled' : color];
102
+ const themeButton = BUTTON_CONFIG[variant][disabled ? 'disabled' : color];
97
103
 
98
104
  return (
99
105
  <Button
@@ -1,4 +1,4 @@
1
- export default {
1
+ export const BUTTON_CONFIG = {
2
2
  contained: {
3
3
  primary: {
4
4
  color: 'white',
@@ -13,7 +13,7 @@ export type JRCValidationButtonProps = JRCButtonProps & {
13
13
  validationMessage?: string;
14
14
  };
15
15
 
16
- /** double click prevented by using a temporary state, changed after a timeout */
16
+ /** double click prevented by using a temporary state (PREVENT_DB_CLICK), changed after a timeout */
17
17
  const state = {
18
18
  NOT_CLICKED: 'NOT_CLICKED',
19
19
  PREVENT_DB_CLICK: 'PREVENT_DB_CLICK',
@@ -44,14 +44,20 @@ export const JRCValidationButton = ({
44
44
 
45
45
  React.useEffect(() => {
46
46
  if (validation === state.PREVENT_DB_CLICK) {
47
- setTimeoutId(setTimeout(() => setValidation(state.NOT_CLICKED), RESET_CLICK_DELAY) as unknown as number);
47
+ setTimeoutId(
48
+ setTimeout(() => {
49
+ setValidation(state.NOT_CLICKED);
50
+ buttonRef?.current?.removeAttribute('style');
51
+ }, RESET_CLICK_DELAY) as unknown as number,
52
+ );
48
53
  }
49
54
  }, [validation]);
50
55
 
51
56
  const validationOnClick = React.useCallback(
52
57
  (e: React.MouseEvent<HTMLButtonElement>) => {
53
58
  if (validationRef.current === state.CLICKED) {
54
- setValidation(state.NOT_CLICKED);
59
+ // Timeout of 0 second to delay the change at the end of the execution pile
60
+ setTimeout(() => setValidation(state.NOT_CLICKED), 0);
55
61
  buttonRef?.current?.removeAttribute('style');
56
62
  onClick && onClick(e);
57
63
  if (timeoutIdRef.current) clearTimeout(timeoutIdRef.current);
@@ -69,7 +75,7 @@ export const JRCValidationButton = ({
69
75
  return (
70
76
  <JRCButton
71
77
  {...props}
72
- type={validation === state.NOT_CLICKED ? 'button' : props.type}
78
+ type={validation === state.CLICKED ? props.type : 'button'}
73
79
  ref={buttonRef}
74
80
  onClick={validationOnClick}
75
81
  icon={validation === state.NOT_CLICKED ? icon : 'icon-fs-question'}
@@ -3,8 +3,7 @@
3
3
  exports[`JRCButton match snapshot 1`] = `
4
4
  <DocumentFragment>
5
5
  <button
6
- class="sc-dkPtRN kttjSS"
7
- color="primary"
6
+ class="sc-jRQBWg LNyd"
8
7
  type="button"
9
8
  >
10
9
  I'm a text
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import styled from 'styled-components';
3
3
  import JRCButton, { transformColor } from '../JRCButton/JRCButton';
4
- import config, { ButtonType } from '../JRCButton/JRCButtonConfig';
4
+ import { BUTTON_CONFIG, ButtonType } from '../JRCButton/JRCButtonConfig';
5
5
  import JRCIcon from '../JRCIcon/JRCIcon';
6
6
  import { ClickAwayListener } from '../Form/Common/ClickAwayListener';
7
7
 
@@ -120,7 +120,7 @@ export const JRCButtonDropdown = ({
120
120
  variant = 'contained',
121
121
  ...props
122
122
  }: JRCButtonDropdownProps) => {
123
- const color = config[variant][props.disabled ? 'disabled' : props.color || 'primary'];
123
+ const color = BUTTON_CONFIG[variant][props.disabled ? 'disabled' : props.color || 'primary'];
124
124
  const [isOpen, setOpenMenu] = React.useState(false);
125
125
  const open = isOpen;
126
126
  const IconRight = ({ color }: any) => {
@@ -0,0 +1,18 @@
1
+ import * as React from 'react';
2
+ import { Meta, Story } from '@storybook/react';
3
+ import { JRCEllipsis, JRCEllipsisProps } from './JRCEllipsis';
4
+
5
+ export default {
6
+ title: 'JRCEllipsis',
7
+ component: JRCEllipsis,
8
+ } as Meta;
9
+
10
+ const Template: Story<JRCEllipsisProps> = (args) => {
11
+ return <JRCEllipsis {...args} />;
12
+ };
13
+
14
+ export const CustomButton = Template.bind({});
15
+ CustomButton.args = {
16
+ label: "Je suis un texte trop long ! Ca marche avec un caractère double tel que : 👋 ! C'est génial !",
17
+ length: 75,
18
+ };
@@ -0,0 +1,22 @@
1
+ import * as React from 'react';
2
+ import { JRCConditionalWrapper } from '../Common/JRCConditionalWrapper';
3
+ import JRCTooltip from '../JRCTooltip/JRCTooltip';
4
+
5
+ export type JRCEllipsisProps = {
6
+ label?: string;
7
+ length: number;
8
+ };
9
+
10
+ export const JRCEllipsis = ({ label, length }: JRCEllipsisProps) => {
11
+ if (!label) return <></>;
12
+
13
+ const labelArr = [...label];
14
+
15
+ return (
16
+ <JRCConditionalWrapper
17
+ condition={labelArr.length > length}
18
+ wrapper={() => <JRCTooltip description={label}>{labelArr.slice(0, length).join('')}…</JRCTooltip>}>
19
+ <>{label}</>
20
+ </JRCConditionalWrapper>
21
+ );
22
+ };
@@ -5,6 +5,7 @@ import { Meta, Story } from '@storybook/react';
5
5
 
6
6
  export default {
7
7
  title: 'JRCHref',
8
+ component: JRCStyledHref,
8
9
  } as Meta;
9
10
 
10
11
  const Template: Story<JRCStyledHrefProps> = (args) => <JRCStyledHref {...args}>{args.children}</JRCStyledHref>;
@@ -14,6 +15,7 @@ Default.args = {
14
15
  children: 'Hello',
15
16
  href: '#',
16
17
  dataCy: 'link-to-the-main-content',
18
+ as: 'a',
17
19
  };
18
20
 
19
21
  const UserTemplate: Story<JRCLinkToProps> = (args) => <JRCLinkToUser {...args}>{args.children}</JRCLinkToUser>;
@@ -1,40 +1,67 @@
1
1
  import * as React from 'react';
2
2
  import styled from 'styled-components';
3
3
  import { DataCy } from '../../types/dataAttributes';
4
+ import { Button } from '../JRCButton/JRCButton';
5
+ import { BUTTON_CONFIG } from '../JRCButton/JRCButtonConfig';
4
6
 
5
7
  /**
6
8
  * Props type for JRCLinkToArticle and JRCLinkToUser
7
- * @member id number : id of the record
8
- * @member href string : href for href
9
- * @member children: enclosed React component
9
+ * @member idObject number : id of the record
10
10
  */
11
- export interface JRCLinkToProps extends JRCStyledHrefProps {
12
- idObject?: number;
13
- href?: string;
14
- children: React.ReactNode;
15
- }
11
+ export type JRCLinkToProps = DataCy &
12
+ React.ComponentPropsWithoutRef<'a'> & {
13
+ idObject?: number;
14
+ };
15
+
16
+ type AnchorProps = {
17
+ as?: 'a';
18
+ };
19
+
20
+ type ButtonProps = {
21
+ variant?: 'contained' | 'outlined';
22
+ color?: 'primary' | 'valid' | 'danger' | 'secondary';
23
+ float?: 'left' | 'right';
24
+ children?: React.ReactNode;
25
+ } & {
26
+ as: 'button';
27
+ };
16
28
 
17
29
  /**
18
30
  * Props type for JRCStyledHref
19
- * @see HTML.a default props
31
+ * @member as render the link styled as a button
20
32
  */
21
- export type JRCStyledHrefProps = DataCy & React.ComponentPropsWithoutRef<'a'>;
33
+ export type JRCStyledHrefProps = DataCy & React.ComponentPropsWithoutRef<'a'> & (AnchorProps | ButtonProps);
22
34
 
23
- export const JRCStyledHref = styled.a.attrs<JRCStyledHrefProps>(({ dataCy, ...props }) => ({
35
+ const Href = styled.a.attrs<DataCy>(({ dataCy, ...props }) => ({
24
36
  'data-cy': dataCy,
25
37
  ...props,
26
38
  }))`
27
39
  color: ${(props) => props.theme.font.hrefColor};
28
40
  `;
29
41
 
42
+ export const JRCStyledHref = (props: JRCStyledHrefProps) => {
43
+ if (props.as === 'button') {
44
+ const { color = 'primary', variant = 'contained', ...restProps } = props;
45
+ const themeButton = BUTTON_CONFIG[variant][color];
46
+
47
+ return (
48
+ <Button {...(restProps as any)} hasChildren={!!props.children} themeButton={themeButton} as="a">
49
+ {props.children}
50
+ </Button>
51
+ );
52
+ }
53
+
54
+ return <Href {...props} />;
55
+ };
56
+
30
57
  export const JRCLinkToArticle = ({ idObject, href, children, ...props }: JRCLinkToProps) => (
31
- <JRCStyledHref href={href ? href : `/article/${idObject}`} {...props}>
58
+ <Href href={href ? href : `/article/${idObject}`} {...props}>
32
59
  {children}
33
- </JRCStyledHref>
60
+ </Href>
34
61
  );
35
62
 
36
63
  export const JRCLinkToUser = ({ idObject, href, children, ...props }: JRCLinkToProps) => (
37
- <JRCStyledHref href={href ? href : `/user/${idObject}`} {...props}>
64
+ <Href href={href ? href : `/user/${idObject}`} {...props}>
38
65
  {children}
39
- </JRCStyledHref>
66
+ </Href>
40
67
  );