pixel-react 1.2.0 → 1.2.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 (36) hide show
  1. package/lib/components/InputWithDropdown/types.d.ts +1 -1
  2. package/lib/components/LabelEditTextField/LabelEditTextField.d.ts +5 -0
  3. package/lib/components/LabelEditTextField/LabelEditTextField.stories.d.ts +11 -0
  4. package/lib/components/LabelEditTextField/index.d.ts +1 -0
  5. package/lib/components/LabelEditTextField/types.d.ts +38 -0
  6. package/lib/components/Select/Select.d.ts +1 -1
  7. package/lib/components/Select/components/Dropdown/Dropdown.d.ts +1 -1
  8. package/lib/components/Select/components/Dropdown/dropdownTypes.d.ts +2 -0
  9. package/lib/components/Select/types.d.ts +11 -4
  10. package/lib/index.d.ts +56 -8
  11. package/lib/index.esm.js +318 -124
  12. package/lib/index.esm.js.map +1 -1
  13. package/lib/index.js +318 -123
  14. package/lib/index.js.map +1 -1
  15. package/lib/tsconfig.tsbuildinfo +1 -1
  16. package/lib/utils/getSelectOptionValue/getSelectOptionValue.d.ts +8 -0
  17. package/package.json +1 -1
  18. package/src/assets/Themes/BaseTheme.scss +8 -1
  19. package/src/assets/Themes/DarkTheme.scss +17 -8
  20. package/src/components/AppHeader/AppHeader.scss +5 -2
  21. package/src/components/Drawer/Drawer.scss +0 -1
  22. package/src/components/Drawer/Drawer.tsx +1 -1
  23. package/src/components/Icon/Icons.scss +1 -0
  24. package/src/components/InputWithDropdown/types.ts +1 -1
  25. package/src/components/LabelEditTextField/LabelEditTextField.scss +85 -0
  26. package/src/components/LabelEditTextField/LabelEditTextField.stories.tsx +136 -0
  27. package/src/components/LabelEditTextField/LabelEditTextField.tsx +207 -0
  28. package/src/components/LabelEditTextField/index.ts +1 -0
  29. package/src/components/LabelEditTextField/types.ts +38 -0
  30. package/src/components/Select/Select.stories.tsx +5 -3
  31. package/src/components/Select/Select.tsx +13 -5
  32. package/src/components/Select/components/Dropdown/Dropdown.tsx +3 -1
  33. package/src/components/Select/components/Dropdown/dropdownTypes.ts +3 -0
  34. package/src/components/Select/types.ts +12 -5
  35. package/src/index.ts +2 -0
  36. package/src/utils/getSelectOptionValue/getSelectOptionValue.ts +31 -0
@@ -0,0 +1,207 @@
1
+ import React, { useState, useRef, useEffect, FC } from 'react';
2
+ import './LabelEditTextField.scss';
3
+ import { LabelEditTextFieldTypes } from './types';
4
+ import Typography from '../Typography';
5
+ import HighlightText from '../HighlightText';
6
+ import Icon from '../Icon';
7
+
8
+ const getErrorMessage = (
9
+ inputValue: string,
10
+ text: string,
11
+ customError?: string
12
+ ): string => {
13
+ if (inputValue === text) {
14
+ return 'No changes were made.';
15
+ } else if (!inputValue) {
16
+ return 'Text is required';
17
+ } else if (inputValue.length < 3) {
18
+ return 'Please enter at least 3 characters.';
19
+ } else if (customError) {
20
+ return customError;
21
+ }
22
+ return '';
23
+ };
24
+ const LabelEditTextField: FC<LabelEditTextFieldTypes> = ({
25
+ label,
26
+ text,
27
+ highlightText,
28
+ customError,
29
+ confirmIcon,
30
+ cancelIcon,
31
+ variant = 'textField',
32
+ dropdownData = [],
33
+ width = '300px',
34
+ height = '22px',
35
+ confirmAction,
36
+ }) => {
37
+ const [isEditing, setIsEditing] = useState(false);
38
+ const [inputValue, setInputValue] = useState(text);
39
+ const [dropdownValue, setDropdownValue] = useState(
40
+ dropdownData[0]?.value ?? ''
41
+ );
42
+ const [showError, setShowError] = useState('');
43
+ const [isTextFieldModified, setIsTextFieldModified] = useState(false);
44
+ const [isDropdownModified, setIsDropdownModified] = useState(false);
45
+ const containerRef = useRef<HTMLDivElement | null>(null);
46
+ const cancelRef = useRef<HTMLDivElement | null>(null); // New ref for cancel icon
47
+
48
+ const handleDoubleClick = () => {
49
+ setIsEditing(true);
50
+ setShowError('');
51
+ };
52
+
53
+ const handleConfirm = () => {
54
+ const errorMessage = getErrorMessage(inputValue, text, customError);
55
+
56
+ if (errorMessage && isEditing) {
57
+ setShowError(errorMessage);
58
+ } else {
59
+ setIsEditing(false);
60
+ setShowError('');
61
+ if (confirmAction) confirmAction(inputValue, dropdownValue);
62
+ }
63
+ };
64
+
65
+ const handleCancel = () => {
66
+ setInputValue(text);
67
+ setDropdownValue(dropdownData[0]?.value ?? '');
68
+ setIsEditing(false);
69
+ setShowError('');
70
+ setIsTextFieldModified(false);
71
+ setIsDropdownModified(false);
72
+ };
73
+
74
+ const handleTextFieldChange = (e: React.ChangeEvent<HTMLInputElement>) => {
75
+ setInputValue(e.target.value);
76
+ setIsTextFieldModified(true);
77
+ setShowError('');
78
+ };
79
+
80
+ const handleDropdownChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
81
+ setDropdownValue(e.target.value);
82
+ setIsDropdownModified(true);
83
+ setShowError('');
84
+ };
85
+
86
+ const handleBlur = (e: MouseEvent) => {
87
+ if (
88
+ containerRef.current &&
89
+ !containerRef.current.contains(e.target as Node) &&
90
+ cancelRef.current !== e.target // Exclude clicks on cancel icon
91
+ ) {
92
+ const errorMessage = getErrorMessage(inputValue, text, customError);
93
+
94
+ if (errorMessage && isEditing) {
95
+ setShowError(errorMessage);
96
+ } else {
97
+ setIsEditing(false);
98
+ setShowError('');
99
+ }
100
+ }
101
+ };
102
+
103
+ useEffect(() => {
104
+ document.addEventListener('click', handleBlur);
105
+ return () => {
106
+ document.removeEventListener('click', handleBlur);
107
+ };
108
+ }, [inputValue]);
109
+
110
+ return (
111
+ <div
112
+ className="ff-label-edit-text-field"
113
+ ref={containerRef}
114
+ style={{ width }}
115
+ >
116
+ {isEditing ? (
117
+ <div className="ff-label-text-field">
118
+ {variant === 'textFieldWithDropdown' ? (
119
+ <div
120
+ className={`ff-label-text-field-with-dropdown ${
121
+ isEditing ? 'open' : ''
122
+ }`}
123
+ style={{ height }}
124
+ >
125
+ <input
126
+ type="text"
127
+ value={inputValue}
128
+ onChange={handleTextFieldChange}
129
+ className={`ff-text-dropdown-field ${
130
+ isTextFieldModified ? 'modified' : ''
131
+ }`}
132
+ placeholder=" "
133
+ style={{
134
+ width,
135
+ }}
136
+ />
137
+ {label && <label className="ff-label">{label}</label>}
138
+ <select
139
+ value={dropdownValue}
140
+ onChange={handleDropdownChange}
141
+ className={`dropdown ${isDropdownModified ? 'modified' : ''}`}
142
+ >
143
+ {dropdownData.map((item) => (
144
+ <option key={item.id} value={item.value}>
145
+ {item.label}
146
+ </option>
147
+ ))}
148
+ </select>
149
+ </div>
150
+ ) : (
151
+ <div className="ff-label-text-field-without-dropdown">
152
+ <input
153
+ type="text"
154
+ value={inputValue}
155
+ onChange={handleTextFieldChange}
156
+ className={`ff-text-field ${
157
+ isTextFieldModified ? 'modified' : ''
158
+ }`}
159
+ placeholder=" "
160
+ style={{ width, height }}
161
+ />
162
+ <label className="ff-textfield-label">{label}</label>
163
+ </div>
164
+ )}
165
+ <div className="ff-icon-container">
166
+ {confirmIcon && (
167
+ <Icon
168
+ color="var(--label-edit-confirm-icon)"
169
+ height={20}
170
+ width={20}
171
+ name={confirmIcon.name}
172
+ className="confirm-icon"
173
+ onClick={handleConfirm}
174
+ />
175
+ )}
176
+ {cancelIcon && (
177
+ <Icon
178
+ color="var(--label-edit-cancel-icon)"
179
+ height={12}
180
+ width={20}
181
+ name={cancelIcon.name}
182
+ className="cancel-icon"
183
+ onClick={handleCancel}
184
+ ref={cancelRef}
185
+ />
186
+ )}
187
+ </div>
188
+ </div>
189
+ ) : (
190
+ <span
191
+ className="display-text"
192
+ onDoubleClick={handleDoubleClick}
193
+ role="button"
194
+ >
195
+ <HighlightText text={inputValue} highlight={highlightText} />
196
+ </span>
197
+ )}
198
+ {showError && isEditing && (
199
+ <Typography as="p" fontSize={8} className="error-text">
200
+ {showError}
201
+ </Typography>
202
+ )}
203
+ </div>
204
+ );
205
+ };
206
+
207
+ export default LabelEditTextField;
@@ -0,0 +1 @@
1
+ export { default } from './LabelEditTextField';
@@ -0,0 +1,38 @@
1
+ export interface IconProps {
2
+ /** Name of the icon to be displayed. */
3
+ name: string;
4
+ /** Optional click handler function for the icon. */
5
+ onClick?: () => void;
6
+ }
7
+ export interface DropdownOption {
8
+ /** Unique identifier for the dropdown option. */
9
+ id: number;
10
+ /** Value associated with the dropdown option. */
11
+ value: string;
12
+ /** Label displayed for the dropdown option. */
13
+ label: string;
14
+ }
15
+ export interface LabelEditTextFieldTypes {
16
+ /** Label text displayed above the input field. */
17
+ label?: string;
18
+ /** Initial text displayed in the input field. */
19
+ text: string;
20
+ /** Text to be highlighted within the displayed text, if provided. */
21
+ highlightText?: string;
22
+ /** Custom error message to be displayed, if applicable. */
23
+ customError?: string;
24
+ /** Confirm icon properties including icon name and click handler. */
25
+ confirmIcon?: IconProps;
26
+ /** Cancel icon properties including icon name and click handler. */
27
+ cancelIcon?: IconProps;
28
+ /** Type of input field - standard text field or text field with a dropdown. */
29
+ variant?: 'textFieldWithDropdown' | 'textField';
30
+ /** Array of dropdown options used if the dropdown variant is selected. */
31
+ dropdownData?: DropdownOption[];
32
+ /** Width of the input field component. */
33
+ width?: string;
34
+ /** Height of the input field component. */
35
+ height?: string;
36
+ /** Function called when confirming input changes, with input and dropdown values as arguments. */
37
+ confirmAction?: (inputValue: string, dropdownValue: string) => void;
38
+ }
@@ -17,10 +17,12 @@ type Story = StoryObj<typeof Select>;
17
17
  export const Primary: Story = {
18
18
  args: {
19
19
  label: 'Select',
20
+ labelAccessor: 'name',
21
+ valueAccessor: 'value',
20
22
  optionsList: [
21
- { label: 'Option 1', value: '1' },
22
- { label: 'Option 2', value: '2' },
23
- { label: 'Option 3', value: '3' },
23
+ { label: 'Option 1', value: '1', name: 'abcd' },
24
+ { label: 'Option 2', value: '2', name: '123' },
25
+ { label: 'Option 3', value: '3', name: '456' },
24
26
  ],
25
27
  },
26
28
  };
@@ -8,6 +8,7 @@ import Icon from '../Icon';
8
8
  import './Select.scss';
9
9
  import usePortalPosition from '../../hooks/usePortalPosition';
10
10
  import Typography from '../Typography';
11
+ import { getValue } from '../../utils/getSelectOptionValue/getSelectOptionValue';
11
12
 
12
13
  const selectReducer = (
13
14
  state: SelectState,
@@ -116,6 +117,8 @@ const Select = ({
116
117
  required = false,
117
118
  optionsRequired = true,
118
119
  selectedOptionColor = 'var(--ff-select-text-color)',
120
+ labelAccessor,
121
+ valueAccessor,
119
122
  }: SelectProps) => {
120
123
  const initialState: SelectState = useMemo(
121
124
  () => ({
@@ -164,7 +167,7 @@ const Select = ({
164
167
  if (actionType === 'SHOW_ERROR' || actionType === 'BLUR_INPUT') {
165
168
  dispatch({
166
169
  type: actionType,
167
- payload: { optionsList, option: selectedOption.value },
170
+ payload: { optionsList, option: getValue(selectedOption) },
168
171
  });
169
172
  } else {
170
173
  dispatch({ type: actionType });
@@ -176,9 +179,10 @@ const Select = ({
176
179
  if (disabled) return;
177
180
  const { value } = e.target;
178
181
  const filteredOptions = optionsList.filter((option) => {
179
- return typeof option.value === 'string'
180
- ? option.value.toLowerCase().includes(value.toLowerCase().trim())
181
- : option.value === Number(value);
182
+ const valueData = getValue(option, valueAccessor);
183
+ return typeof valueData === 'string'
184
+ ? valueData.toLowerCase().includes(value.toLowerCase().trim())
185
+ : valueData === Number(value);
182
186
  });
183
187
  dispatch({ type: 'UPDATE_OPTION_LIST', payload: filteredOptions });
184
188
  dispatch({ type: 'UPDATE_OPTION', payload: value });
@@ -197,7 +201,10 @@ const Select = ({
197
201
  const onSelectOptionSelector = (option: Option) => {
198
202
  if (!disabled) {
199
203
  onSelectBlur();
200
- dispatch({ type: 'UPDATE_OPTION', payload: option.value });
204
+ dispatch({
205
+ type: 'UPDATE_OPTION',
206
+ payload: getValue(option, valueAccessor),
207
+ });
201
208
  if (onChange) {
202
209
  onChange(option);
203
210
  }
@@ -361,6 +368,7 @@ const Select = ({
361
368
  options={options}
362
369
  optionZIndex={optionZIndex}
363
370
  inputRef={InputRef}
371
+ labelAccessor={labelAccessor}
364
372
  />,
365
373
  document.body
366
374
  )}
@@ -7,6 +7,7 @@ import Typography from '../../../Typography';
7
7
  import { ffid } from '../../../../utils/ffID/ffid';
8
8
  import { ThemeContext } from '../../../ThemeProvider/ThemeProvider';
9
9
  import classNames from 'classnames';
10
+ import { getLabel } from '../../../../utils/getSelectOptionValue/getSelectOptionValue';
10
11
 
11
12
  const Dropdown = ({
12
13
  onSelectBlur,
@@ -15,6 +16,7 @@ const Dropdown = ({
15
16
  options = [],
16
17
  optionZIndex = 100,
17
18
  inputRef,
19
+ labelAccessor,
18
20
  }: DropDownListProps) => {
19
21
  const themeContext = useContext(ThemeContext);
20
22
  const currentTheme = themeContext?.currentTheme;
@@ -71,7 +73,7 @@ const Dropdown = ({
71
73
  color="var(--ff-select-text-color)"
72
74
  onClick={() => onSelectOptionSelector(option)}
73
75
  >
74
- {option.label}
76
+ {getLabel(option, labelAccessor)}
75
77
  </Typography>
76
78
  ))
77
79
  ) : (
@@ -7,6 +7,8 @@ export interface DropDownListProps {
7
7
  options?: Option[];
8
8
  optionZIndex?: number;
9
9
  inputRef?: React.RefObject<HTMLInputElement>;
10
+ labelAccessor?: string;
11
+ valueAccessor?: string;
10
12
  }
11
13
 
12
14
  export const dropdownDefaultCSSData = {
@@ -17,3 +19,4 @@ export const dropdownDefaultCSSData = {
17
19
  // Future use case if we provide padding-top, padding-bottom for option wrapper
18
20
  dropDownWrapperPadding: 0,
19
21
  };
22
+
@@ -1,5 +1,3 @@
1
- import { ReactNode } from 'react';
2
-
3
1
  export interface SelectProps {
4
2
  /*
5
3
  * Label for the select dropdown
@@ -69,6 +67,15 @@ export interface SelectProps {
69
67
  * selectedOptionColor prop provides the custom color for the selected option
70
68
  */
71
69
  selectedOptionColor?: string;
70
+
71
+ /**
72
+ * Label accessor
73
+ */
74
+ labelAccessor?: string;
75
+ /**
76
+ * Value accessor
77
+ */
78
+ valueAccessor?: string;
72
79
  }
73
80
 
74
81
  export interface DrowdownPosition {
@@ -124,8 +131,8 @@ export type SelectAction =
124
131
  };
125
132
  };
126
133
 
134
+ type OptionValue = any;
135
+
127
136
  export interface Option {
128
- label: string | ReactNode;
129
- value: string;
130
- disabled?: boolean;
137
+ [key: string]: OptionValue;
131
138
  }
package/src/index.ts CHANGED
@@ -50,6 +50,7 @@ import IconRadioGroup from './components/IconRadioGroup';
50
50
  import MachineInputField from './components/MachineInputField';
51
51
  import SequentialConnectingBranch from './components/SequentialConnectingBranch';
52
52
  import AttachmentButton from './components/AttachmentButton';
53
+ import LabelEditTextField from './components/LabelEditTextField';
53
54
 
54
55
  // Utils imports
55
56
  import { checkEmpty } from './utils/checkEmpty/checkEmpty';
@@ -120,6 +121,7 @@ export {
120
121
  MachineInputField,
121
122
  SequentialConnectingBranch,
122
123
  AttachmentButton,
124
+ LabelEditTextField,
123
125
  IconRadioGroup,
124
126
 
125
127
  // utils exports
@@ -0,0 +1,31 @@
1
+ type DynamicValues = any;
2
+
3
+ interface dynamicObject {
4
+ [key: string]: DynamicValues;
5
+ }
6
+
7
+ type accessorType = string | undefined;
8
+
9
+ export const getLabel = (
10
+ option: dynamicObject,
11
+ accessor: accessorType = ''
12
+ ) => {
13
+ if (!accessor) {
14
+ if (option.hasOwnProperty('label')) {
15
+ return option.label;
16
+ }
17
+ }
18
+ return option[accessor];
19
+ };
20
+
21
+ export const getValue = (
22
+ option: dynamicObject,
23
+ accessor: accessorType = ''
24
+ ) => {
25
+ if (!accessor) {
26
+ if (option.hasOwnProperty('value')) {
27
+ return option.label;
28
+ }
29
+ }
30
+ return option[accessor];
31
+ };