@axinom/mosaic-ui 0.68.1 → 0.68.3

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/dist/components/Buttons/Button/Button.d.ts.map +1 -1
  2. package/dist/components/Buttons/Button.model.d.ts +2 -0
  3. package/dist/components/Buttons/Button.model.d.ts.map +1 -1
  4. package/dist/components/DynamicDataList/DynamicListDataEntry/DynamicListDataEntry.d.ts.map +1 -1
  5. package/dist/components/DynamicDataList/DynamicListDataEntry/Renderers/createInputRenderer/createInputRenderer.d.ts +1 -1
  6. package/dist/components/DynamicDataList/DynamicListDataEntry/Renderers/createInputRenderer/createInputRenderer.d.ts.map +1 -1
  7. package/dist/components/DynamicDataList/DynamicListDataEntry/Renderers/createSelectRenderer/createSelectRenderer.d.ts +1 -1
  8. package/dist/components/DynamicDataList/DynamicListDataEntry/Renderers/createSelectRenderer/createSelectRenderer.d.ts.map +1 -1
  9. package/dist/components/DynamicDataList/DynamicListDataEntry/Renderers/renderers.model.d.ts +2 -0
  10. package/dist/components/DynamicDataList/DynamicListDataEntry/Renderers/renderers.model.d.ts.map +1 -1
  11. package/dist/components/Explorer/BulkEdit/FormFieldsConfigConverter.d.ts.map +1 -1
  12. package/dist/components/Explorer/BulkEdit/helpers/FieldWrapper.d.ts.map +1 -1
  13. package/dist/components/FieldSelection/FieldSelection.d.ts +1 -0
  14. package/dist/components/FieldSelection/FieldSelection.d.ts.map +1 -1
  15. package/dist/components/FormStation/FormStationHeader/FormStationHeader.d.ts.map +1 -1
  16. package/dist/components/InfoPanel/hooks/useCollapse.d.ts.map +1 -1
  17. package/dist/index.es.js +3 -3
  18. package/dist/index.es.js.map +1 -1
  19. package/dist/index.js +4 -4
  20. package/dist/index.js.map +1 -1
  21. package/package.json +2 -2
  22. package/src/components/Buttons/Button/Button.tsx +4 -0
  23. package/src/components/Buttons/Button.model.ts +2 -0
  24. package/src/components/DynamicDataList/DynamicListDataEntry/DynamicListDataEntry.tsx +38 -20
  25. package/src/components/DynamicDataList/DynamicListDataEntry/Renderers/createInputRenderer/createInputRenderer.tsx +2 -0
  26. package/src/components/DynamicDataList/DynamicListDataEntry/Renderers/createSelectRenderer/createSelectRenderer.tsx +2 -0
  27. package/src/components/DynamicDataList/DynamicListDataEntry/Renderers/renderers.model.ts +2 -0
  28. package/src/components/Explorer/BulkEdit/FormFieldsConfigConverter.tsx +1 -0
  29. package/src/components/Explorer/BulkEdit/helpers/FieldWrapper.scss +1 -0
  30. package/src/components/Explorer/BulkEdit/helpers/FieldWrapper.tsx +1 -0
  31. package/src/components/FieldSelection/FieldSelection.scss +1 -1
  32. package/src/components/FieldSelection/FieldSelection.tsx +6 -2
  33. package/src/components/FormStation/FormStationHeader/FormStationHeader.tsx +5 -3
  34. package/src/components/Icons/Icons.tsx +1 -1
  35. package/src/components/InfoPanel/InfoPanel.scss +2 -0
  36. package/src/components/InfoPanel/hooks/useCollapse.ts +9 -7
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axinom/mosaic-ui",
3
- "version": "0.68.1",
3
+ "version": "0.68.3",
4
4
  "description": "UI components for building Axinom Mosaic applications",
5
5
  "author": "Axinom",
6
6
  "license": "PROPRIETARY",
@@ -112,5 +112,5 @@
112
112
  "publishConfig": {
113
113
  "access": "public"
114
114
  },
115
- "gitHead": "197e3de6ae3702028a53e14b1d99430df93d16f7"
115
+ "gitHead": "496e5fc981424ef5a5a4eb2a6b4bfb55cd5f7019"
116
116
  }
@@ -69,6 +69,7 @@ const ContextButtonElement = React.forwardRef<
69
69
  className = '',
70
70
  customIcon,
71
71
  dataTestId,
72
+ title,
72
73
  onButtonClicked = noop,
73
74
  onBlur,
74
75
  }: ContextButtonProps,
@@ -93,6 +94,7 @@ const ContextButtonElement = React.forwardRef<
93
94
  disabled={disabled}
94
95
  onBlur={onBlur}
95
96
  data-test-id={dataTestId ?? 'button'}
97
+ title={title}
96
98
  >
97
99
  {customIcon ? customIcon : <Icons icon={icon} />}
98
100
  </button>
@@ -113,6 +115,7 @@ const NavigationButtonElement = React.forwardRef<
113
115
  dataTestId = undefined,
114
116
  disabled = false,
115
117
  openInNewTab = false,
118
+ title,
116
119
  }: NavigationButtonProps,
117
120
  ref: ForwardedRef<HTMLAnchorElement>,
118
121
  ) {
@@ -132,6 +135,7 @@ const NavigationButtonElement = React.forwardRef<
132
135
  style={{ height, width }}
133
136
  data-test-id={dataTestId ?? 'button'}
134
137
  target={openInNewTab ? '_blank' : undefined}
138
+ title={title}
135
139
  >
136
140
  {customIcon ? customIcon : <Icons icon={icon ? icon : defaultIcon} />}
137
141
  </Link>
@@ -104,4 +104,6 @@ interface CommonButtonOptions {
104
104
  className?: string;
105
105
  /** Optional data-test-id */
106
106
  dataTestId?: string;
107
+ /** Optional title attribute for the button */
108
+ title?: string;
107
109
  }
@@ -1,5 +1,11 @@
1
1
  import clsx from 'clsx';
2
- import React, { PropsWithChildren, ReactElement, useState } from 'react';
2
+ import React, {
3
+ PropsWithChildren,
4
+ ReactElement,
5
+ useCallback,
6
+ useEffect,
7
+ useState,
8
+ } from 'react';
3
9
  import { ValidationError } from 'yup';
4
10
  import { OptionalObjectSchema } from 'yup/lib/object';
5
11
  import { noop } from '../../../helpers/utils';
@@ -105,29 +111,41 @@ export const DynamicListDataEntry = <T extends Data>({
105
111
  const [isDirty, setIsDirty] = useState<boolean>(false);
106
112
  const [error, setError] = useState<Record<string, string>>({});
107
113
 
108
- const validateSchema = async (data: T): Promise<boolean> => {
109
- if (!rowValidationSchema) {
110
- return true;
111
- }
114
+ const validateSchema = useCallback(
115
+ async (data: T): Promise<boolean> => {
116
+ if (!rowValidationSchema) {
117
+ return true;
118
+ }
112
119
 
113
- try {
114
- await rowValidationSchema.validate(data, { abortEarly: false });
115
- setError({});
120
+ try {
121
+ await rowValidationSchema.validate(data, { abortEarly: false });
122
+ setError({});
123
+
124
+ return true;
125
+ } catch (e) {
126
+ const newErrors: Record<string, string> = {};
127
+ (e as ValidationError).inner.forEach((validationError) => {
128
+ const path = validationError.path;
129
+ if (path !== undefined && newErrors?.[path] === undefined) {
130
+ newErrors[path] = validationError.message;
131
+ }
132
+ });
133
+ setError(newErrors);
116
134
 
117
- return true;
118
- } catch (e) {
119
- const newErrors: Record<string, string> = {};
120
- (e as ValidationError).inner.forEach((validationError) => {
121
- const path = validationError.path;
122
- if (path !== undefined && newErrors?.[path] === undefined) {
123
- newErrors[path] = validationError.message;
124
- }
125
- });
126
- setError(newErrors);
135
+ return false;
136
+ }
137
+ },
138
+ [rowValidationSchema],
139
+ );
127
140
 
128
- return false;
141
+ // Input errors should be cleared when the field is disabled
142
+ useEffect(() => {
143
+ if (disabled) {
144
+ setError({});
145
+ } else if (isDirty) {
146
+ validateSchema(state);
129
147
  }
130
- };
148
+ }, [disabled, state, isDirty, validateSchema]);
131
149
 
132
150
  /**
133
151
  * Updates new data object with supplied key/value pair
@@ -14,6 +14,7 @@ export const createInputRenderer = ({
14
14
  name = '',
15
15
  placeholder,
16
16
  type = 'text',
17
+ autofocus = false,
17
18
  transform = (value: string) => value,
18
19
  }: CreateInputRendererConfig = {}): DynamicListDataEntryRenderer => {
19
20
  const inputRenderer = (
@@ -38,6 +39,7 @@ export const createInputRenderer = ({
38
39
  error={error}
39
40
  className={classes.container}
40
41
  value={(currentValue as string) ?? ''}
42
+ autoFocus={autofocus}
41
43
  />
42
44
  );
43
45
  };
@@ -22,6 +22,7 @@ export const createSelectRenderer = ({
22
22
  options = [],
23
23
  defaultValue,
24
24
  transform = (value: string) => value,
25
+ autofocus = false,
25
26
  }: CreateSelectRendererConfig = {}): DynamicListDataEntryRenderer => {
26
27
  const selectRenderer = (
27
28
  currentValue: unknown,
@@ -49,6 +50,7 @@ export const createSelectRenderer = ({
49
50
  className={classes.container}
50
51
  value={(currentValue as string) ?? ''}
51
52
  options={options}
53
+ autoFocus={autofocus}
52
54
  />
53
55
  );
54
56
  };
@@ -9,6 +9,8 @@ export interface BaseRendererConfig {
9
9
  placeholder?: string;
10
10
  /** Optional transformer that will change the final value to match the return value */
11
11
  transform?: (value: string) => unknown;
12
+ /** Optional autofocus on render (default: false) */
13
+ autofocus?: boolean;
12
14
  }
13
15
 
14
16
  export interface CreateInputRendererConfig extends BaseRendererConfig {
@@ -76,6 +76,7 @@ export const BulkEditFormFieldsConfigConverter = (
76
76
  <FieldSelection
77
77
  onFieldRemoved={onFieldRemoved}
78
78
  onFieldAdded={onFieldAdded}
79
+ name="bulkedit-fields"
79
80
  >
80
81
  {fields}
81
82
  </FieldSelection>
@@ -1,6 +1,7 @@
1
1
  .container {
2
2
  display: grid;
3
3
  grid-template-columns: auto min-content;
4
+ gap: 5px;
4
5
  }
5
6
 
6
7
  .button {
@@ -84,6 +84,7 @@ export const FieldWrapper: React.FC<FieldWrapperProps> = ({
84
84
  icon={IconName.ClearAll}
85
85
  buttonContext={clearField ? ButtonContext.Active : ButtonContext.Icon}
86
86
  onButtonClicked={handleClearFieldChange}
87
+ title="Clear"
87
88
  />
88
89
  )}
89
90
  </div>
@@ -27,5 +27,5 @@
27
27
  }
28
28
 
29
29
  .content {
30
- padding: 10px;
30
+ padding: 10px 0;
31
31
  }
@@ -9,6 +9,7 @@ import { IconName } from '../Icons';
9
9
  import classes from './FieldSelection.scss';
10
10
 
11
11
  interface FieldSelectionProps {
12
+ name?: string;
12
13
  className?: string;
13
14
  onFieldAdded?: (field: string) => void;
14
15
  onFieldRemoved?: (field: string) => void;
@@ -22,6 +23,7 @@ interface FieldDefinition {
22
23
  }
23
24
 
24
25
  export const FieldSelection: React.FC<FieldSelectionProps> = ({
26
+ name,
25
27
  className,
26
28
  onFieldAdded = noop,
27
29
  onFieldRemoved = noop,
@@ -62,6 +64,7 @@ export const FieldSelection: React.FC<FieldSelectionProps> = ({
62
64
  expandedByDefault={true}
63
65
  header={
64
66
  <FieldSelectionHeader
67
+ name={name}
65
68
  fields={availableFields}
66
69
  onAddField={(value) => {
67
70
  const newField = availableFields.find(
@@ -108,9 +111,10 @@ export const FieldSelection: React.FC<FieldSelectionProps> = ({
108
111
  };
109
112
 
110
113
  const FieldSelectionHeader: React.FC<{
114
+ name?: string;
111
115
  fields: SelectOption[];
112
116
  onAddField: (value: string) => void;
113
- }> = ({ fields, onAddField }) => {
117
+ }> = ({ name, fields, onAddField }) => {
114
118
  const [value, setValue] = React.useState<string>();
115
119
 
116
120
  useEffect(() => {
@@ -124,7 +128,7 @@ const FieldSelectionHeader: React.FC<{
124
128
  <div className={classes.selectionHeader}>
125
129
  <Select
126
130
  label="Field"
127
- name="field"
131
+ name={name ?? 'field'}
128
132
  placeholder="Select Field"
129
133
  options={fields}
130
134
  disabled={fields.length === 0}
@@ -40,7 +40,7 @@ export const FormStationHeader: React.FC<
40
40
  },
41
41
  setValidationError,
42
42
  }) => {
43
- const { dirty, submitForm, resetForm, isValid } =
43
+ const { dirty, submitForm, resetForm, validateForm } =
44
44
  useFormikContext<FormikValues>();
45
45
  const quickEditContext = useContext(QuickEditContext);
46
46
  const bulkEditContext = useContext(BulkEditContext);
@@ -74,10 +74,12 @@ export const FormStationHeader: React.FC<
74
74
  if (quickEditContext?.isQuickEditMode) {
75
75
  quickEditContext.refresh();
76
76
  } else if (bulkEditContext?.isBulkEditMode) {
77
- if (!isValid) {
77
+ const errors = await validateForm();
78
+ if (Object.keys(errors).length > 0) {
78
79
  setValidationError();
79
80
  return;
80
81
  }
82
+
81
83
  await submitForm();
82
84
  history.replace(history.location.pathname);
83
85
  } else {
@@ -128,7 +130,6 @@ export const FormStationHeader: React.FC<
128
130
  cancelNavigationUrl,
129
131
  dirty,
130
132
  history,
131
- isValid,
132
133
  quickEditContext,
133
134
  resetForm,
134
135
  saveHeaderActionConfig.icon,
@@ -136,6 +137,7 @@ export const FormStationHeader: React.FC<
136
137
  setValidationError,
137
138
  showSaveHeaderAction,
138
139
  submitForm,
140
+ validateForm,
139
141
  ]);
140
142
 
141
143
  return (
@@ -175,7 +175,7 @@ const ClearAllIcon: React.FC<SvgElementProps> = (props) => (
175
175
  <SvgElement {...props}>
176
176
  <path
177
177
  {...DEFAULT_PATH_PROPS}
178
- d="M32.7,20c0,7-5.7,12.7-12.7,12.7s-12.7-5.7-12.7-12.7,5.7-12.7,12.7-12.7,12.7,5.7,12.7,12.7ZM34,6L6,34"
178
+ d="M28.6,33.7H11.2l-7.2-7.2L24.2,6.3l11.8,11.8-15.6,15.6M18.6,11.9l11.8,11.8M31.9,33.7h3.7"
179
179
  />
180
180
  </SvgElement>
181
181
  );
@@ -98,6 +98,8 @@
98
98
  .content {
99
99
  transform: translateX(0);
100
100
  pointer-events: none;
101
+ max-height: 0;
102
+ overflow: hidden;
101
103
  }
102
104
 
103
105
  .filler {
@@ -180,13 +180,15 @@ export const useCollapse = ({
180
180
  userExpandedRef.current = true;
181
181
  autoCollapsedRef.current = false;
182
182
 
183
- setTimeout(() => {
184
- containerRef.current?.scrollIntoView({
185
- behavior: 'smooth',
186
- block: 'nearest',
187
- inline: 'end',
188
- });
189
- }, 350);
183
+ // TODO: Temporarily disable auto-scrolling to prevent jarring experience when collapsing/expanding panels in smaller screens.
184
+ // to be revisited with https://dev.azure.com/axinom/CMS/_workitems/edit/53479/
185
+ // setTimeout(() => {
186
+ // containerRef.current?.scrollIntoView({
187
+ // behavior: 'smooth',
188
+ // block: 'nearest',
189
+ // inline: 'end',
190
+ // });
191
+ // }, 350);
190
192
  }
191
193
 
192
194
  onCollapseChange?.(newState);