@axinom/mosaic-ui 0.66.0-rc.10 → 0.66.0-rc.12

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 (76) hide show
  1. package/dist/components/DynamicDataList/DynamicListDataEntry/DynamicListDataEntry.d.ts.map +1 -1
  2. package/dist/components/DynamicDataList/DynamicListRow/DynamicListRow.d.ts.map +1 -1
  3. package/dist/components/Filters/Filter/Filter.d.ts.map +1 -1
  4. package/dist/components/Filters/Filters.model.d.ts +5 -0
  5. package/dist/components/Filters/Filters.model.d.ts.map +1 -1
  6. package/dist/components/Filters/SelectionTypes/DateTimeFilter/DateTimeFilter.d.ts +2 -0
  7. package/dist/components/Filters/SelectionTypes/DateTimeFilter/DateTimeFilter.d.ts.map +1 -1
  8. package/dist/components/Filters/SelectionTypes/FreeTextFilter/FreeTextFilter.d.ts +2 -0
  9. package/dist/components/Filters/SelectionTypes/FreeTextFilter/FreeTextFilter.d.ts.map +1 -1
  10. package/dist/components/Filters/SelectionTypes/MultiOptionFilter/MultiOptionFilter.d.ts +2 -0
  11. package/dist/components/Filters/SelectionTypes/MultiOptionFilter/MultiOptionFilter.d.ts.map +1 -1
  12. package/dist/components/Filters/SelectionTypes/NumericTextFilter/NumericTextFilter.d.ts +2 -0
  13. package/dist/components/Filters/SelectionTypes/NumericTextFilter/NumericTextFilter.d.ts.map +1 -1
  14. package/dist/components/Filters/SelectionTypes/OptionsFilter/OptionsFilter.d.ts +2 -0
  15. package/dist/components/Filters/SelectionTypes/OptionsFilter/OptionsFilter.d.ts.map +1 -1
  16. package/dist/components/Filters/SelectionTypes/SearcheableOptionsFilter/SearcheableOptionsFilter.d.ts +2 -0
  17. package/dist/components/Filters/SelectionTypes/SearcheableOptionsFilter/SearcheableOptionsFilter.d.ts.map +1 -1
  18. package/dist/components/FormElements/Radio/Radio.d.ts.map +1 -1
  19. package/dist/components/FormElements/ToggleButton/ToggleButton.d.ts.map +1 -1
  20. package/dist/components/Hub/Tile/Tile.d.ts.map +1 -1
  21. package/dist/components/Icons/Icons.d.ts +4 -9
  22. package/dist/components/Icons/Icons.d.ts.map +1 -1
  23. package/dist/components/LandingPageTiles/TileLarge/TileLarge.d.ts.map +1 -1
  24. package/dist/components/LandingPageTiles/TileSmall/TileSmall.d.ts.map +1 -1
  25. package/dist/components/List/ListHeader/ColumnLabel/ColumnLabel.d.ts.map +1 -1
  26. package/dist/components/Loaders/ImageLoader/ImageLoader.d.ts.map +1 -1
  27. package/dist/components/PageHeader/PageHeaderAction/PageHeaderAction.d.ts.map +1 -1
  28. package/dist/components/VisualElements/ImgElement.d.ts +50 -0
  29. package/dist/components/VisualElements/ImgElement.d.ts.map +1 -0
  30. package/dist/components/VisualElements/SvgElement.d.ts +14 -0
  31. package/dist/components/VisualElements/SvgElement.d.ts.map +1 -0
  32. package/dist/components/VisualElements/index.d.ts +3 -0
  33. package/dist/components/VisualElements/index.d.ts.map +1 -0
  34. package/dist/components/index.d.ts +1 -0
  35. package/dist/components/index.d.ts.map +1 -1
  36. package/dist/index.es.js +4 -4
  37. package/dist/index.es.js.map +1 -1
  38. package/dist/index.js +4 -4
  39. package/dist/index.js.map +1 -1
  40. package/package.json +2 -2
  41. package/src/components/DynamicDataList/DynamicListDataEntry/DynamicListDataEntry.spec.tsx +3 -3
  42. package/src/components/DynamicDataList/DynamicListDataEntry/DynamicListDataEntry.tsx +4 -1
  43. package/src/components/DynamicDataList/DynamicListRow/DynamicListRow.tsx +4 -1
  44. package/src/components/Filters/Filter/Filter.scss +1 -1
  45. package/src/components/Filters/Filter/Filter.tsx +27 -1
  46. package/src/components/Filters/Filters.model.ts +6 -0
  47. package/src/components/Filters/SelectionTypes/DateTimeFilter/DateTimeFilter.tsx +5 -0
  48. package/src/components/Filters/SelectionTypes/FreeTextFilter/FreeTextFilter.tsx +4 -0
  49. package/src/components/Filters/SelectionTypes/MultiOptionFilter/MultiOptionFilter.tsx +9 -1
  50. package/src/components/Filters/SelectionTypes/NumericTextFilter/NumericTextFilter.tsx +5 -0
  51. package/src/components/Filters/SelectionTypes/OptionsFilter/OptionsFilter.tsx +8 -0
  52. package/src/components/Filters/SelectionTypes/SearcheableOptionsFilter/SearcheableOptionsFilter.tsx +6 -1
  53. package/src/components/FormElements/Radio/Radio.tsx +3 -2
  54. package/src/components/FormElements/ToggleButton/ToggleButton.tsx +32 -27
  55. package/src/components/Hub/Hub.stories.tsx +3 -2
  56. package/src/components/Hub/Tile/Tile.spec.tsx +7 -2
  57. package/src/components/Hub/Tile/Tile.tsx +2 -1
  58. package/src/components/Icons/Icons.scss +1 -0
  59. package/src/components/Icons/Icons.spec.tsx +90 -41
  60. package/src/components/Icons/Icons.tsx +357 -765
  61. package/src/components/LandingPageTiles/LandingPageTiles.stories.tsx +3 -2
  62. package/src/components/LandingPageTiles/TileLarge/TileLarge.spec.tsx +5 -1
  63. package/src/components/LandingPageTiles/TileLarge/TileLarge.tsx +2 -1
  64. package/src/components/LandingPageTiles/TileSmall/TileSmall.spec.tsx +7 -2
  65. package/src/components/LandingPageTiles/TileSmall/TileSmall.tsx +2 -1
  66. package/src/components/List/ListHeader/ColumnLabel/ColumnLabel.spec.tsx +2 -2
  67. package/src/components/List/ListHeader/ColumnLabel/ColumnLabel.tsx +5 -12
  68. package/src/components/Loaders/ImageLoader/ImageLoader.spec.tsx +13 -14
  69. package/src/components/Loaders/ImageLoader/ImageLoader.tsx +5 -3
  70. package/src/components/PageHeader/PageHeaderAction/PageHeaderAction.tsx +13 -2
  71. package/src/components/VisualElements/ImgElement.spec.tsx +92 -0
  72. package/src/components/VisualElements/ImgElement.tsx +72 -0
  73. package/src/components/VisualElements/SvgElement.spec.tsx +160 -0
  74. package/src/components/VisualElements/SvgElement.tsx +40 -0
  75. package/src/components/VisualElements/index.ts +7 -0
  76. package/src/components/index.ts +1 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axinom/mosaic-ui",
3
- "version": "0.66.0-rc.10",
3
+ "version": "0.66.0-rc.12",
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": "b485f96f99a96fc75fa706ec4b3afa224ce8610c"
115
+ "gitHead": "a2cef21028c76a6c1e0a02c77ee63c819ca48746"
116
116
  }
@@ -95,7 +95,7 @@ describe('DynamicListDataEntry', () => {
95
95
  />,
96
96
  );
97
97
 
98
- const row = wrapper.find('.container');
98
+ const row = wrapper.find('.container').first();
99
99
 
100
100
  expect(row.prop('style')).toHaveProperty('gridColumnGap', undefined);
101
101
  expect(row.prop('style')).toHaveProperty('alignItems', undefined);
@@ -113,7 +113,7 @@ describe('DynamicListDataEntry', () => {
113
113
  it('sets styling props when passed in', () => {
114
114
  const wrapper = shallow(<DynamicListDataEntry {...defaultProps} />);
115
115
 
116
- const row = wrapper.find('.container');
116
+ const row = wrapper.find('.container').first();
117
117
 
118
118
  expect(row.prop('style')).toHaveProperty(
119
119
  'gridColumnGap',
@@ -256,7 +256,7 @@ describe('DynamicListDataEntry', () => {
256
256
  />,
257
257
  );
258
258
 
259
- const input = wrapper.find('input');
259
+ const input = wrapper.find('input').first();
260
260
 
261
261
  expect(input.prop('disabled')).toBe(true);
262
262
  });
@@ -9,6 +9,7 @@ import { ObjectSchemaDefinition } from '../../FormStation';
9
9
  import { IconName, Icons } from '../../Icons';
10
10
  import { DynamicListColumn } from '../DynamicDataList.model';
11
11
  import classes from './DynamicListDataEntry.scss';
12
+ import { createInputRenderer } from './Renderers';
12
13
 
13
14
  export enum DynamicListDataEntryMode {
14
15
  Add,
@@ -274,6 +275,8 @@ const renderField = function <T extends Data>(
274
275
  disabled,
275
276
  );
276
277
  } else {
277
- return '';
278
+ return createInputRenderer({
279
+ placeholder: 'Enter value',
280
+ })(currentValue, error, onValueChanged, disabled);
278
281
  }
279
282
  };
@@ -202,7 +202,10 @@ export const DynamicListRow = <T extends Data>({
202
202
  );
203
203
  })}
204
204
  {showActionColumn && (
205
- <div className={classes.actionButtonContainer}>
205
+ <div
206
+ className={classes.actionButtonContainer}
207
+ onClick={(e) => e.stopPropagation()}
208
+ >
206
209
  {inlineMenuData && !!inlineMenuData.length && (
207
210
  <InlineMenu
208
211
  actions={inlineMenuData}
@@ -63,7 +63,7 @@ $input-inactive: 1px solid var(--input-border-color, $input-border-color);
63
63
  }
64
64
  }
65
65
 
66
- span {
66
+ label {
67
67
  padding-left: 15px;
68
68
  }
69
69
  }
@@ -64,6 +64,15 @@ export const Filter = <T extends Data>({
64
64
  const contentRef = useRef<HTMLDivElement>(null);
65
65
  const valueRef = useRef<HTMLDivElement>(null);
66
66
 
67
+ const inputId = `${String(options.property)}-filter-input`;
68
+ const labelId = `${String(options.property)}-filter-label`;
69
+ const inputBasedFilter = [
70
+ FilterTypes.FreeText,
71
+ FilterTypes.Numeric,
72
+ FilterTypes.Date,
73
+ FilterTypes.DateTime,
74
+ ];
75
+
67
76
  useEffect(() => {
68
77
  setContentHeight(
69
78
  (contentRef.current?.scrollHeight ?? 0) +
@@ -135,6 +144,7 @@ export const Filter = <T extends Data>({
135
144
  onError={onError}
136
145
  onValidate={onValidate}
137
146
  selectOnFocus={selectOnFocus}
147
+ inputId={inputId}
138
148
  />
139
149
  );
140
150
 
@@ -147,6 +157,7 @@ export const Filter = <T extends Data>({
147
157
  }
148
158
  onError={onError}
149
159
  onValidate={onValidate}
160
+ inputId={inputId}
150
161
  />
151
162
  );
152
163
 
@@ -158,6 +169,7 @@ export const Filter = <T extends Data>({
158
169
  onSelect={(value: FilterValue) =>
159
170
  onFilterValueChange(options.property, value)
160
171
  }
172
+ labelId={labelId}
161
173
  />
162
174
  );
163
175
 
@@ -169,6 +181,7 @@ export const Filter = <T extends Data>({
169
181
  onSelect={(value: FilterValue) =>
170
182
  onFilterValueChange(options.property, value?.toString())
171
183
  }
184
+ labelId={labelId}
172
185
  />
173
186
  );
174
187
  case FilterTypes.SearcheableOptions:
@@ -181,6 +194,8 @@ export const Filter = <T extends Data>({
181
194
  onFilterValueChange(options.property, value, stringValue)
182
195
  }
183
196
  maxItems={options.maxItems}
197
+ inputId={inputId}
198
+ labelId={labelId}
184
199
  />
185
200
  );
186
201
 
@@ -194,6 +209,7 @@ export const Filter = <T extends Data>({
194
209
  modifyTime={false}
195
210
  onError={onError}
196
211
  onValidate={onValidate}
212
+ inputId={inputId}
197
213
  />
198
214
  );
199
215
 
@@ -207,6 +223,7 @@ export const Filter = <T extends Data>({
207
223
  modifyTime={true}
208
224
  onError={onError}
209
225
  onValidate={onValidate}
226
+ inputId={inputId}
210
227
  />
211
228
  );
212
229
 
@@ -220,6 +237,7 @@ export const Filter = <T extends Data>({
220
237
  }}
221
238
  onError={onError}
222
239
  onValidate={onValidate}
240
+ labelId={labelId}
223
241
  />
224
242
  );
225
243
 
@@ -249,7 +267,15 @@ export const Filter = <T extends Data>({
249
267
  )}
250
268
  data-test-id="filter-button-toggle"
251
269
  >
252
- <span data-test-id="filter-label">{options.label}</span>
270
+ <label
271
+ id={labelId}
272
+ {...(inputBasedFilter.includes(options.type)
273
+ ? {}
274
+ : { htmlFor: inputId })}
275
+ data-test-id="filter-label"
276
+ >
277
+ {options.label}
278
+ </label>
253
279
  <Button
254
280
  icon={IconName.ChevronDown}
255
281
  className={clsx(classes.button)}
@@ -78,6 +78,12 @@ export interface CustomFilterProps {
78
78
  /** Wether or not the filter is active (default: false) */
79
79
  active?: boolean;
80
80
 
81
+ /**
82
+ * Optional id for the label element.
83
+ * Assign to the label's `id` and reference from the input or group via `aria-labelledby`.
84
+ */
85
+ labelId?: string;
86
+
81
87
  /** Callback triggered when a new filter value is selected */
82
88
  onSelect: (
83
89
  value: FilterValue,
@@ -31,6 +31,9 @@ export interface DateTimeFilterProps {
31
31
 
32
32
  /** CSS Class name for additional styles */
33
33
  className?: string;
34
+
35
+ /** Optional id for the input field */
36
+ inputId?: string;
34
37
  }
35
38
 
36
39
  export const DateTimeFilter: React.FC<DateTimeFilterProps> = ({
@@ -40,6 +43,7 @@ export const DateTimeFilter: React.FC<DateTimeFilterProps> = ({
40
43
  onError = noop,
41
44
  onValidate: customValidate,
42
45
  className = '',
46
+ inputId,
43
47
  }) => {
44
48
  const container = useRef<HTMLDivElement>(null);
45
49
  const [showPicker, setShowPicker] = useState(false);
@@ -111,6 +115,7 @@ export const DateTimeFilter: React.FC<DateTimeFilterProps> = ({
111
115
  ref={container}
112
116
  >
113
117
  <input
118
+ id={inputId}
114
119
  autoFocus
115
120
  className={clsx(classes.inputValue)}
116
121
  onKeyDown={handleKeyDown}
@@ -19,6 +19,8 @@ export interface FreeTextFilterProps {
19
19
  className?: string;
20
20
  /** Select text on focus if true (default: true) */
21
21
  selectOnFocus?: boolean;
22
+ /** Optional id for the input field */
23
+ inputId?: string;
22
24
  }
23
25
 
24
26
  export const FreeTextFilter: React.FC<FreeTextFilterProps> = ({
@@ -28,6 +30,7 @@ export const FreeTextFilter: React.FC<FreeTextFilterProps> = ({
28
30
  onValidate: customValidate,
29
31
  className = '',
30
32
  selectOnFocus = true,
33
+ inputId,
31
34
  }) => {
32
35
  const [errorMsg, setErrorMsg] = useState<string>();
33
36
  const ENTER_KEY = 'Enter';
@@ -68,6 +71,7 @@ export const FreeTextFilter: React.FC<FreeTextFilterProps> = ({
68
71
  )}
69
72
  >
70
73
  <input
74
+ id={inputId}
71
75
  ref={inputRef}
72
76
  autoFocus
73
77
  className={clsx(classes.inputValue, errorMsg && classes.hasError)}
@@ -17,19 +17,27 @@ export interface MultiOptionFilterProps {
17
17
 
18
18
  /** CSS Class name for additional styles */
19
19
  className?: string;
20
+
21
+ /** ID for the label */
22
+ labelId?: string;
20
23
  }
21
24
 
22
25
  export const MultiOptionsFilter: React.FC<MultiOptionFilterProps> = ({
23
26
  value,
24
27
  options,
25
28
  onSelect,
29
+ labelId,
26
30
  }) => {
27
31
  const [selectedOptionList, setSelectedOptionList] = useState<string[]>(
28
32
  value ? value.split(',') : [],
29
33
  );
30
34
 
31
35
  return (
32
- <div className={clsx(classes.multiFilterContainer)}>
36
+ <div
37
+ className={classes.multiFilterContainer}
38
+ role="group"
39
+ aria-labelledby={labelId}
40
+ >
33
41
  {options?.map((option: Option) => (
34
42
  <Checkbox
35
43
  className={clsx(
@@ -18,6 +18,9 @@ export interface NumericTextFilterProps {
18
18
 
19
19
  /** CSS Class name for additional styles */
20
20
  className?: string;
21
+
22
+ /** Optional id for the input field */
23
+ inputId?: string;
21
24
  }
22
25
 
23
26
  export const NumericTextFilter: React.FC<NumericTextFilterProps> = ({
@@ -26,6 +29,7 @@ export const NumericTextFilter: React.FC<NumericTextFilterProps> = ({
26
29
  onError = noop,
27
30
  onValidate: customValidate,
28
31
  className = '',
32
+ inputId,
29
33
  }) => {
30
34
  const [errorMsg, setErrorMsg] = useState<string>();
31
35
  const ENTER_KEY = 'Enter';
@@ -64,6 +68,7 @@ export const NumericTextFilter: React.FC<NumericTextFilterProps> = ({
64
68
  )}
65
69
  >
66
70
  <input
71
+ id={inputId}
67
72
  autoFocus
68
73
  value={valueLocal}
69
74
  className={clsx(classes.inputValue, errorMsg && classes.hasError)}
@@ -14,6 +14,9 @@ export interface OptionFilterProps {
14
14
 
15
15
  /** CSS Class name for additional styles */
16
16
  className?: string;
17
+
18
+ /** Optional id for the label element */
19
+ labelId?: string;
17
20
  }
18
21
 
19
22
  export const OptionsFilter: React.FC<OptionFilterProps> = ({
@@ -21,9 +24,12 @@ export const OptionsFilter: React.FC<OptionFilterProps> = ({
21
24
  options,
22
25
  onSelect,
23
26
  className = '',
27
+ labelId,
24
28
  }) => {
25
29
  return (
26
30
  <div
31
+ role="radiogroup"
32
+ aria-labelledby={labelId}
27
33
  className={clsx(classes.container, 'options-filter-container', className)}
28
34
  >
29
35
  {options?.map((option: Option) => (
@@ -32,6 +38,8 @@ export const OptionsFilter: React.FC<OptionFilterProps> = ({
32
38
  onClick={() => onSelect(option.value)}
33
39
  key={option.label}
34
40
  data-test-value={option.value as string}
41
+ role="radio"
42
+ aria-checked={value === option.value}
35
43
  >
36
44
  <span
37
45
  className={clsx(value === option.value && classes.selected)}
@@ -14,6 +14,9 @@ interface SearcheableOptionFilterProps
14
14
  searchInputPlaceholder?: string;
15
15
  maxItems?: number;
16
16
  onSelect: (value: string, stringValue?: string) => void;
17
+
18
+ /** Optional id for the input field */
19
+ inputId?: string;
17
20
  }
18
21
 
19
22
  export const SearcheableOptionsFilter: React.FC<
@@ -24,6 +27,7 @@ export const SearcheableOptionsFilter: React.FC<
24
27
  maxItems = 10,
25
28
  onSelect,
26
29
  className,
30
+ inputId,
27
31
  ...rest
28
32
  }) => {
29
33
  const [options, setOptions] = useState<Option[]>([]);
@@ -56,9 +60,10 @@ export const SearcheableOptionsFilter: React.FC<
56
60
  )}
57
61
  >
58
62
  <input
63
+ id={inputId}
59
64
  autoFocus
60
65
  onChange={debounce(onChangeHandler, 500)}
61
- className={clsx(classes.inputValue, '' && classes.hasError)}
66
+ className={classes.inputValue}
62
67
  placeholder={searchInputPlaceholder}
63
68
  />
64
69
  </div>
@@ -5,6 +5,7 @@ import {
5
5
  ConfirmationConfig,
6
6
  ConfirmationMode,
7
7
  } from '../../ConfirmDialog';
8
+ import { SvgElement } from '../../VisualElements/SvgElement';
8
9
  import { BaseFormControl, BaseInputEvents } from '../Form.models';
9
10
  import { FormElementContainer } from '../FormElementContainer';
10
11
  import classes from './Radio.scss';
@@ -42,7 +43,7 @@ export const RadioButton: React.FC<RadioButtonProps> = ({
42
43
  confirmValue,
43
44
  disabled = false,
44
45
  }) => (
45
- <svg
46
+ <SvgElement
46
47
  className={clsx(classes.buttonContainer, {
47
48
  [classes.checked]: checked,
48
49
  [classes.hasError]: hasError,
@@ -59,7 +60,7 @@ export const RadioButton: React.FC<RadioButtonProps> = ({
59
60
  >
60
61
  <circle className={classes.radioOutline} cx="17" cy="17" r="15" />
61
62
  <circle className={classes.radioDot} cx="17" cy="17" r="7.5" />
62
- </svg>
63
+ </SvgElement>
63
64
  );
64
65
 
65
66
  /**
@@ -7,6 +7,7 @@ import {
7
7
  ConfirmationConfig,
8
8
  ConfirmationMode,
9
9
  } from '../../ConfirmDialog';
10
+ import { SvgElement } from '../../VisualElements/SvgElement';
10
11
  import { BaseFormControl } from '../Form.models';
11
12
  import { FormElementContainer } from '../FormElementContainer';
12
13
  import classes from './ToggleButton.scss';
@@ -119,43 +120,22 @@ export const ToggleButton: React.FC<ToggleButtonProps> = ({
119
120
  data-test-checked={selected}
120
121
  >
121
122
  <div className={clsx(classes.off, { [classes.active]: !selected })}>
122
- <svg
123
- version="1.1"
124
- xmlns="http://www.w3.org/2000/svg"
125
- viewBox="0 0 40 40"
126
- >
123
+ <SvgElement viewBox="0 0 40 40">
127
124
  {showONOFFText && !selected && (
128
125
  <g className={classes.svgText}>
129
- <path
130
- d="M6.5,20.1c0-1,0.2-1.9,0.5-2.6c0.2-0.5,0.5-1,0.9-1.4c0.4-0.4,0.8-0.7,1.3-0.9c0.6-0.3,1.4-0.4,2.2-0.4
131
- c1.5,0,2.7,0.5,3.6,1.4c0.9,0.9,1.3,2.2,1.3,3.8c0,1.6-0.4,2.9-1.3,3.8c-0.9,0.9-2.1,1.4-3.6,1.4c-1.5,0-2.7-0.5-3.6-1.4
132
- S6.5,21.7,6.5,20.1z M8.6,20c0,1.1,0.3,2,0.8,2.6c0.5,0.6,1.2,0.9,2,0.9c0.8,0,1.5-0.3,2-0.9c0.5-0.6,0.8-1.5,0.8-2.6
133
- c0-1.1-0.3-2-0.8-2.6s-1.2-0.8-2-0.8c-0.8,0-1.5,0.3-2,0.9C8.8,18,8.6,18.8,8.6,20z"
134
- ></path>
135
- <path d="M17.9,25V15h6.9v1.7H20v2.4h4.2v1.7H20V25H17.9z"></path>
136
- <path d="M26.6,25V15h6.9v1.7h-4.9v2.4h4.2v1.7h-4.2V25H26.6z"></path>
126
+ <OffIcon />
137
127
  </g>
138
128
  )}
139
- </svg>
129
+ </SvgElement>
140
130
  </div>
141
131
  <div className={clsx(classes.on, { [classes.active]: selected })}>
142
- <svg
143
- version="1.1"
144
- xmlns="http://www.w3.org/2000/svg"
145
- viewBox="0 0 40 40"
146
- >
132
+ <SvgElement viewBox="0 0 40 40">
147
133
  {showONOFFText && selected && (
148
134
  <g className={classes.svgText}>
149
- <path
150
- d="M10.4,20.1c0-1,0.2-1.9,0.5-2.6c0.2-0.5,0.5-1,0.9-1.4s0.8-0.7,1.3-0.9c0.6-0.3,1.3-0.4,2.2-0.4
151
- c1.5,0,2.7,0.5,3.5,1.4s1.3,2.2,1.3,3.8c0,1.6-0.4,2.9-1.3,3.8c-0.9,0.9-2.1,1.4-3.5,1.4c-1.5,0-2.7-0.5-3.6-1.4
152
- C10.8,22.9,10.4,21.7,10.4,20.1z M12.4,20c0,1.1,0.3,2,0.8,2.6c0.5,0.6,1.2,0.9,2,0.9s1.5-0.3,2-0.9C17.7,22,18,21.1,18,20
153
- c0-1.1-0.3-2-0.8-2.6s-1.2-0.8-2-0.8s-1.5,0.3-2,0.9C12.7,18,12.4,18.8,12.4,20z"
154
- ></path>
155
- <path d="M21.7,25V15h2l4.1,6.7V15h1.9v10h-2l-4-6.5V25H21.7z"></path>
135
+ <OnIcon />
156
136
  </g>
157
137
  )}
158
- </svg>
138
+ </SvgElement>
159
139
  </div>
160
140
  </button>
161
141
  {confirmation && (
@@ -176,3 +156,28 @@ export const ToggleButton: React.FC<ToggleButtonProps> = ({
176
156
  </>
177
157
  );
178
158
  };
159
+
160
+ const OffIcon: React.FC = () => (
161
+ <>
162
+ <path
163
+ d="M6.5,20.1c0-1,0.2-1.9,0.5-2.6c0.2-0.5,0.5-1,0.9-1.4c0.4-0.4,0.8-0.7,1.3-0.9c0.6-0.3,1.4-0.4,2.2-0.4
164
+ c1.5,0,2.7,0.5,3.6,1.4c0.9,0.9,1.3,2.2,1.3,3.8c0,1.6-0.4,2.9-1.3,3.8c-0.9,0.9-2.1,1.4-3.6,1.4c-1.5,0-2.7-0.5-3.6-1.4
165
+ S6.5,21.7,6.5,20.1z M8.6,20c0,1.1,0.3,2,0.8,2.6c0.5,0.6,1.2,0.9,2,0.9c0.8,0,1.5-0.3,2-0.9c0.5-0.6,0.8-1.5,0.8-2.6
166
+ c0-1.1-0.3-2-0.8-2.6s-1.2-0.8-2-0.8c-0.8,0-1.5,0.3-2,0.9C8.8,18,8.6,18.8,8.6,20z"
167
+ />
168
+ <path d="M17.9,25V15h6.9v1.7H20v2.4h4.2v1.7H20V25H17.9z" />
169
+ <path d="M26.6,25V15h6.9v1.7h-4.9v2.4h4.2v1.7h-4.2V25H26.6z" />
170
+ </>
171
+ );
172
+
173
+ const OnIcon: React.FC = () => (
174
+ <>
175
+ <path
176
+ d="M10.4,20.1c0-1,0.2-1.9,0.5-2.6c0.2-0.5,0.5-1,0.9-1.4s0.8-0.7,1.3-0.9c0.6-0.3,1.3-0.4,2.2-0.4
177
+ c1.5,0,2.7,0.5,3.5,1.4s1.3,2.2,1.3,3.8c0,1.6-0.4,2.9-1.3,3.8c-0.9,0.9-2.1,1.4-3.5,1.4c-1.5,0-2.7-0.5-3.6-1.4
178
+ C10.8,22.9,10.4,21.7,10.4,20.1z M12.4,20c0,1.1,0.3,2,0.8,2.6c0.5,0.6,1.2,0.9,2,0.9s1.5-0.3,2-0.9C17.7,22,18,21.1,18,20
179
+ c0-1.1-0.3-2-0.8-2.6s-1.2-0.8-2-0.8s-1.5,0.3-2,0.9C12.7,18,12.4,18.8,12.4,20z"
180
+ />
181
+ <path d="M21.7,25V15h2l4.1,6.7V15h1.9v10h-2l-4-6.5V25H21.7z" />
182
+ </>
183
+ );
@@ -1,6 +1,7 @@
1
1
  import { Meta, StoryObj } from '@storybook/react';
2
2
  import React from 'react';
3
3
  import { MemoryRouter } from 'react-router-dom';
4
+ import { SvgElement } from '../VisualElements/SvgElement';
4
5
  import { Hub } from './Hub';
5
6
  import { HubItem } from './Hub.model';
6
7
 
@@ -14,7 +15,7 @@ const defaultContainer = {
14
15
 
15
16
  const DefaultIcon: React.FC = () => {
16
17
  return (
17
- <svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40">
18
+ <SvgElement viewBox="0 0 40 40">
18
19
  <path
19
20
  vectorEffect="non-scaling-stroke"
20
21
  fill="none"
@@ -22,7 +23,7 @@ const DefaultIcon: React.FC = () => {
22
23
  d="M39,39H1V1h38V39z M39.1,26l-8.4-8.7l-9.1,11.5
23
24
  l-6.4-5.4L3.6,39.1 M12.8,7.8c-2.4,0-4.4,2-4.4,4.4s2,4.4,4.4,4.4s4.4-2,4.4-4.4S15.3,7.8,12.8,7.8z"
24
25
  />
25
- </svg>
26
+ </SvgElement>
26
27
  );
27
28
  };
28
29
 
@@ -1,5 +1,6 @@
1
- import { shallow } from 'enzyme';
1
+ import { mount, shallow } from 'enzyme';
2
2
  import React from 'react';
3
+ import { MemoryRouter } from 'react-router-dom'; // Add this import
3
4
  import { Tile } from './Tile';
4
5
  import { TileProps } from './Tile.model';
5
6
 
@@ -31,7 +32,11 @@ describe('Tile', () => {
31
32
  });
32
33
 
33
34
  it('renders an icon when passed as a string URL', () => {
34
- const wrapper = shallow(<Tile {...mockProps} />);
35
+ const wrapper = mount(
36
+ <MemoryRouter>
37
+ <Tile {...mockProps} />
38
+ </MemoryRouter>,
39
+ );
35
40
  const iconUrl = wrapper.find('img').prop('src');
36
41
 
37
42
  expect(iconUrl).toBe(`${mockProps.icon}`);
@@ -1,6 +1,7 @@
1
1
  import clsx from 'clsx';
2
2
  import React from 'react';
3
3
  import { Link } from 'react-router-dom';
4
+ import { ImgElement } from '../../VisualElements/ImgElement';
4
5
  import { TileProps } from './Tile.model';
5
6
  import classes from './Tile.scss';
6
7
 
@@ -34,7 +35,7 @@ export const Tile: React.FC<TileProps> = ({
34
35
  {React.isValidElement(icon)
35
36
  ? icon
36
37
  : typeof icon === 'string' && (
37
- <img src={icon} alt={`${label} icon`} />
38
+ <ImgElement src={icon} decorative={true} />
38
39
  )}
39
40
  </div>
40
41
  <div className={classes.label}>
@@ -5,6 +5,7 @@
5
5
 
6
6
  height: 35px;
7
7
 
8
+ rect,
8
9
  polygon,
9
10
  polyline,
10
11
  path,