@zohodesk/components 1.6.0 → 1.6.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.
package/README.md CHANGED
@@ -2,6 +2,19 @@
2
2
 
3
3
  Dot UI is a customizable React component library built to deliver a clean, accessible, and developer-friendly UI experience. It offers a growing set of reusable components designed to align with modern design systems and streamline application development across projects.
4
4
 
5
+
6
+ # 1.6.1
7
+
8
+ - **Label Component - v1**
9
+ - The label component of v1 has been updated with accessibility enhancements and few props have been renamed and restructured to align better with accessibility guidelines
10
+ - Mandatory field accessibility has been improved by allowing the mandatory indicator type (asterisk or text) to be handled via user preferences.
11
+ - Explicit boolean props are introduced: isRequired, isDisabled, isReadOnly - These replace earlier state handling that was implicitly managed through the palette prop.
12
+ - Custom Class Structure has been updated to provide separate styling control : Label element, Container wrapper
13
+ - The variant prop has been replaced with textWeight to clearly define typography styling (regular / semibold).
14
+ - The dataId has been replaced with testId, customId
15
+
16
+ - These updates have been applied across all form labels using Label v1
17
+
5
18
  # 1.6.0
6
19
 
7
20
  - Updated @zohodesk-private/color-variable-preprocessor package - Grayscale introduced
@@ -18,7 +31,6 @@ Dot UI is a customizable React component library built to deliver a clean, acces
18
31
  - **Select**
19
32
  - Removed the size prop from InputFieldLine usage, as it is unused.
20
33
 
21
-
22
34
  # 1.5.8
23
35
 
24
36
  - Memoized **Portal** logic to improve performance.
@@ -8,4 +8,10 @@
8
8
  --zdt_v1_switch_onLabel: hsla(217, calc(var(--zd-saturation, 1) * 23.08%), calc(17.84% + var(--zdc_default)), calc(1 + var(--zdc_alpha_high)));
9
9
  --zdt_v1_switch_offLabel: hsla(217, calc(var(--zd-saturation, 1) * 13.73%), calc(70.00% + var(--zdc_default_inverse_medium)), calc(1 + var(--zdc_alpha_low)));
10
10
  --zdt_v1_switch_offLabel_hover: hsla(210, calc(var(--zd-saturation, 1) * 7.41%), calc(89.41% + var(--zdc_default_inverse)), calc(1 + var(--zdc_alpha_high)));
11
+
12
+ /* label */
13
+ --zdt_v1_label_text: hsla(210, calc(var(--zd-saturation, 1) * 7.41%), calc(89.41% + var(--zdc_default_inverse)), calc(1 + var(--zdc_alpha_high)));
14
+ --zdt_v1_label_required_text: hsla(0, calc(var(--zd-saturation, 1) * 100.00%), calc(70.98% + var(--zdc_default_inverse_low)), calc(1 + var(--zdc_alpha_low)));
15
+ --zdt_v1_label_disabled_text: var(--dot_inputDisable);
16
+ --zdt_v1_label_secondary_text: hsla(217, calc(var(--zd-saturation, 1) * 13.73%), calc(70.00% + var(--zdc_default_inverse)), calc(1 + var(--zdc_alpha_high)));
11
17
  }
@@ -8,4 +8,10 @@
8
8
  --zdt_v1_switch_onLabel: hsla(0, calc(var(--zd-saturation, 1) * 0.00%), calc(100.00% + var(--zdc_default)), calc(1 + var(--zdc_alpha_high)));
9
9
  --zdt_v1_switch_offLabel: hsla(220, calc(var(--zd-saturation, 1) * 10.45%), calc(39.41% + var(--zdc_default_inverse_medium)), calc(1 + var(--zdc_alpha_low)));
10
10
  --zdt_v1_switch_offLabel_hover: hsla(0, calc(var(--zd-saturation, 1) * 0.00%), calc(0.00% + var(--zdc_default_inverse)), calc(1 + var(--zdc_alpha_high)));
11
+
12
+ /* label */
13
+ --zdt_v1_label_text: hsla(0, calc(var(--zd-saturation, 1) * 0.00%), calc(0.00% + var(--zdc_default_inverse)), calc(1 + var(--zdc_alpha_high)));
14
+ --zdt_v1_label_required_text: hsla(0, calc(var(--zd-saturation, 1) * 71.91%), calc(53.92% + var(--zdc_default_inverse_low)), calc(1 + var(--zdc_alpha_low)));
15
+ --zdt_v1_label_disabled_text: var(--dot_inputDisable);
16
+ --zdt_v1_label_secondary_text: hsla(220, calc(var(--zd-saturation, 1) * 10.45%), calc(39.41% + var(--zdc_default_inverse)), calc(1 + var(--zdc_alpha_high)));
11
17
  }
@@ -8,4 +8,10 @@
8
8
  --zdt_v1_switch_onLabel: hsla(0, calc(var(--zd-saturation, 1) * 0.00%), calc(12.94% + var(--zdc_default)), calc(1 + var(--zdc_alpha_high)));
9
9
  --zdt_v1_switch_offLabel: hsla(0, calc(var(--zd-saturation, 1) * 0.00%), calc(60.00% + var(--zdc_default_inverse_medium)), calc(1 + var(--zdc_alpha_low)));
10
10
  --zdt_v1_switch_offLabel_hover: hsla(210, calc(var(--zd-saturation, 1) * 7.41%), calc(89.41% + var(--zdc_default_inverse)), calc(1 + var(--zdc_alpha_high)));
11
+
12
+ /* label */
13
+ --zdt_v1_label_text: hsla(210, calc(var(--zd-saturation, 1) * 7.41%), calc(89.41% + var(--zdc_default_inverse)), calc(1 + var(--zdc_alpha_high)));
14
+ --zdt_v1_label_required_text: hsla(0, calc(var(--zd-saturation, 1) * 100.00%), calc(70.98% + var(--zdc_default_inverse_low)), calc(1 + var(--zdc_alpha_low)));
15
+ --zdt_v1_label_disabled_text: var(--dot_inputDisable);
16
+ --zdt_v1_label_secondary_text: hsla(0, calc(var(--zd-saturation, 1) * 0.00%), calc(60.00% + var(--zdc_default_inverse)), calc(1 + var(--zdc_alpha_high)));
11
17
  }
@@ -11,6 +11,8 @@ const LibraryContextProvider = _ref => {
11
11
  hasTagColorInheritedToText,
12
12
  shouldIndicateSwitchState,
13
13
  shouldStrikeThroughDisabledButton,
14
+ labelRequiredType,
15
+ shouldHighlightRequiredLabel,
14
16
  children
15
17
  } = _ref;
16
18
  const [value, setValue] = useState({
@@ -19,7 +21,9 @@ const LibraryContextProvider = _ref => {
19
21
  coloredTagVariant,
20
22
  hasTagColorInheritedToText,
21
23
  shouldIndicateSwitchState,
22
- shouldStrikeThroughDisabledButton
24
+ shouldStrikeThroughDisabledButton,
25
+ labelRequiredType,
26
+ shouldHighlightRequiredLabel
23
27
  });
24
28
 
25
29
  function setGlobalContext(key, data) {
@@ -44,7 +48,9 @@ LibraryContextProvider.propTypes = {
44
48
  hasTagColorInheritedToText: PropTypes.bool,
45
49
  direction: PropTypes.string,
46
50
  shouldIndicateSwitchState: PropTypes.bool,
47
- shouldStrikeThroughDisabledButton: PropTypes.bool
51
+ shouldStrikeThroughDisabledButton: PropTypes.bool,
52
+ labelRequiredType: PropTypes.oneOf(['asterisk', 'text']),
53
+ shouldHighlightRequiredLabel: PropTypes.bool
48
54
  };
49
55
  LibraryContextProvider.defaultProps = {
50
56
  isReducedMotion: getLibraryConfig('isReducedMotion'),
@@ -52,6 +58,8 @@ LibraryContextProvider.defaultProps = {
52
58
  coloredTagVariant: 'bold',
53
59
  hasTagColorInheritedToText: true,
54
60
  shouldIndicateSwitchState: false,
55
- shouldStrikeThroughDisabledButton: false
61
+ shouldStrikeThroughDisabledButton: false,
62
+ labelRequiredType: 'asterisk',
63
+ shouldHighlightRequiredLabel: true
56
64
  };
57
65
  export default LibraryContextProvider;
@@ -1,40 +1,103 @@
1
- import React from 'react';
2
- import { defaultProps } from "./props/defaultProps";
3
- import { propTypes } from "./props/propTypes";
4
- import style from "../../Label/Label.module.css";
5
- import colors from "../../Label/LabelColors.module.css";
6
- export default function Label(props) {
1
+ import React from 'react'; // props
2
+
3
+ import defaultProps from "./props/defaultProps";
4
+ import propTypes from "./props/propTypes"; // methods
5
+
6
+ import cssJSLogic from "./css/cssJSLogic";
7
+ import { renderNode } from '@zohodesk/utils'; // hoc
8
+
9
+ import { withComponentRegistrar } from '@zohodesk/dotkit/es/react/ComponentRegistry'; // constants
10
+
11
+ import { DISPLAY_MODE, FONT_SIZE } from "./constants"; // components
12
+
13
+ import Flex from '@zohodesk/layout/es/Flex/Flex';
14
+ import Typography from "../../Typography/Typography"; // css
15
+
16
+ import style from "./css/Label_v1.module.css";
17
+
18
+ const Label = props => {
7
19
  const {
8
- text,
9
- type,
10
- palette,
11
- size,
12
- clipped,
13
20
  htmlFor,
14
- title,
15
21
  onClick,
16
- dataId,
17
- dataSelectorId,
18
- variant,
19
- customClass,
20
- id,
21
- a11y = {}
22
+ text,
23
+ customId,
24
+ testId,
25
+ tagAttributes,
26
+ a11yAttributes,
27
+ customProps,
28
+ isRequired,
29
+ requiredType,
30
+ isClipped,
31
+ size,
32
+ fontWeight,
33
+ layout,
34
+ renderRightPlaceholderNode,
35
+ renderLeftPlaceholderNode,
36
+ i18nKeys
22
37
  } = props;
23
38
  const {
24
- tabIndex
25
- } = a11y;
26
- return /*#__PURE__*/React.createElement("label", {
27
- className: `${style.label} ${style[type]} ${style[size]} ${colors[palette]} ${style[`font_${variant}`]}
28
- ${clipped ? style.dotted : ''} ${onClick ? style.pointer : style.cursor} ${customClass} `,
29
- htmlFor: htmlFor,
30
- "data-title": title,
31
- "data-id": dataId,
32
- "data-test-id": dataId,
33
- "data-selector-id": dataSelectorId,
34
- onClick: onClick,
35
- id: id,
36
- tabIndex: tabIndex
37
- }, text);
38
- }
39
+ container: tagAttributes_container,
40
+ label: tagAttributes_label
41
+ } = tagAttributes;
42
+ const {
43
+ container: a11yAttributes_container,
44
+ label: a11yAttributes_label
45
+ } = a11yAttributes;
46
+ const {
47
+ container: customProps_container,
48
+ label: customProps_label
49
+ } = customProps;
50
+ const {
51
+ requiredText: i18n_requiredText = 'Required'
52
+ } = i18nKeys;
53
+ const {
54
+ labelClass,
55
+ requiredClass,
56
+ containerClass
57
+ } = cssJSLogic({
58
+ props,
59
+ style
60
+ });
61
+ const a11yAttributes_requiredText = {
62
+ 'aria-hidden': 'true'
63
+ };
64
+
65
+ const renderRequired = () => /*#__PURE__*/React.createElement(Typography, {
66
+ $ui_tagName: "span",
67
+ $ui_className: requiredClass,
68
+ $a11yAttributes_text: a11yAttributes_requiredText,
69
+ $ui_size: FONT_SIZE[size],
70
+ $ui_weight: fontWeight,
71
+ $i18n_dataTitle: ""
72
+ }, requiredType === 'text' ? `(${i18n_requiredText})` : '*');
73
+
74
+ return /*#__PURE__*/React.createElement(Flex, { ...customProps_container,
75
+ $tagAttributes_container: { ...tagAttributes_container,
76
+ 'data-selector-id': customId
77
+ },
78
+ $ui_displayMode: DISPLAY_MODE[layout],
79
+ $ui_alignItems: "center",
80
+ $a11yAttributes_container: a11yAttributes_container,
81
+ testId: testId,
82
+ customId: customId,
83
+ $ui_className: containerClass
84
+ }, renderNode(renderLeftPlaceholderNode), /*#__PURE__*/React.createElement(Typography, { ...customProps_label,
85
+ $tagAttributes_text: { ...tagAttributes_label,
86
+ htmlFor,
87
+ onClick
88
+ },
89
+ $ui_tagName: "label",
90
+ $ui_className: labelClass,
91
+ $i18n_dataTitle: text,
92
+ $a11yAttributes_text: a11yAttributes_label,
93
+ $ui_size: FONT_SIZE[size],
94
+ $ui_weight: fontWeight,
95
+ $flag_dotted: isClipped
96
+ }, text, isRequired && !isClipped ? renderRequired() : null), isRequired && isClipped ? renderRequired() : null, renderNode(renderRightPlaceholderNode));
97
+ };
98
+
99
+ export default withComponentRegistrar(Label, {
100
+ name: 'ZDC_V1_Label'
101
+ });
39
102
  Label.defaultProps = defaultProps;
40
103
  Label.propTypes = propTypes;
@@ -0,0 +1,214 @@
1
+ import React from 'react';
2
+ import { render, cleanup } from '@testing-library/react';
3
+ import Label from "../Label"; // Hoisted enum values (PropTypes.oneOf enforcement)
4
+
5
+ const REQUIRED_TYPE_VALUES = ['asterisk', 'text'];
6
+ const SIZE_VALUES = ['small', 'medium'];
7
+ const PALETTE_VALUES = ['default', 'secondary'];
8
+ const TEXT_WEIGHT_VALUES = ['regular', 'semibold'];
9
+ const LAYOUT_VALUES = ['inline', 'block'];
10
+ afterEach(cleanup);
11
+ describe('Label - Snapshot', () => {
12
+ test('Render with default props', () => {
13
+ const {
14
+ asFragment
15
+ } = render( /*#__PURE__*/React.createElement(Label, {
16
+ text: "Label text"
17
+ }));
18
+ expect(asFragment()).toMatchSnapshot();
19
+ });
20
+ test.each(REQUIRED_TYPE_VALUES)('Render with requiredType=%s', value => {
21
+ const {
22
+ asFragment
23
+ } = render( /*#__PURE__*/React.createElement(Label, {
24
+ text: "Label text",
25
+ isRequired: true,
26
+ requiredType: value
27
+ }));
28
+ expect(asFragment()).toMatchSnapshot();
29
+ });
30
+ test.each(SIZE_VALUES)('Render with size=%s', value => {
31
+ const {
32
+ asFragment
33
+ } = render( /*#__PURE__*/React.createElement(Label, {
34
+ text: "Label text",
35
+ size: value
36
+ }));
37
+ expect(asFragment()).toMatchSnapshot();
38
+ });
39
+ test.each(PALETTE_VALUES)('Render with palette=%s', value => {
40
+ const {
41
+ asFragment
42
+ } = render( /*#__PURE__*/React.createElement(Label, {
43
+ text: "Label text",
44
+ palette: value
45
+ }));
46
+ expect(asFragment()).toMatchSnapshot();
47
+ });
48
+ test.each(TEXT_WEIGHT_VALUES)('Render with fontWeight=%s', value => {
49
+ const {
50
+ asFragment
51
+ } = render( /*#__PURE__*/React.createElement(Label, {
52
+ text: "Label text",
53
+ fontWeight: value
54
+ }));
55
+ expect(asFragment()).toMatchSnapshot();
56
+ });
57
+ test.each(LAYOUT_VALUES)('Render with layout=%s', value => {
58
+ const {
59
+ asFragment
60
+ } = render( /*#__PURE__*/React.createElement(Label, {
61
+ text: "Label text",
62
+ layout: value
63
+ }));
64
+ expect(asFragment()).toMatchSnapshot();
65
+ });
66
+ test('Render with shouldHighlightRequired=true', () => {
67
+ const {
68
+ asFragment
69
+ } = render( /*#__PURE__*/React.createElement(Label, {
70
+ text: "Label text",
71
+ isRequired: true,
72
+ shouldHighlightRequired: true
73
+ }));
74
+ expect(asFragment()).toMatchSnapshot();
75
+ });
76
+ test('Render with isClipped=true', () => {
77
+ const {
78
+ asFragment
79
+ } = render( /*#__PURE__*/React.createElement(Label, {
80
+ text: "Label text",
81
+ isClipped: true
82
+ }));
83
+ expect(asFragment()).toMatchSnapshot();
84
+ });
85
+ test('Render with isRequired=true', () => {
86
+ const {
87
+ asFragment
88
+ } = render( /*#__PURE__*/React.createElement(Label, {
89
+ text: "Label text",
90
+ isRequired: true
91
+ }));
92
+ expect(asFragment()).toMatchSnapshot();
93
+ });
94
+ test('Render with isRequired=true isClipped=true', () => {
95
+ const {
96
+ asFragment
97
+ } = render( /*#__PURE__*/React.createElement(Label, {
98
+ text: "Label text",
99
+ isRequired: true,
100
+ isClipped: true
101
+ }));
102
+ expect(asFragment()).toMatchSnapshot();
103
+ });
104
+ test('Render with isDisabled=true', () => {
105
+ const {
106
+ asFragment
107
+ } = render( /*#__PURE__*/React.createElement(Label, {
108
+ text: "Label text",
109
+ isDisabled: true
110
+ }));
111
+ expect(asFragment()).toMatchSnapshot();
112
+ });
113
+ test('Render with isReadOnly=true', () => {
114
+ const {
115
+ asFragment
116
+ } = render( /*#__PURE__*/React.createElement(Label, {
117
+ text: "Label text",
118
+ isReadOnly: true
119
+ }));
120
+ expect(asFragment()).toMatchSnapshot();
121
+ });
122
+ test('Render with htmlFor', () => {
123
+ const {
124
+ asFragment
125
+ } = render( /*#__PURE__*/React.createElement(Label, {
126
+ text: "Label text",
127
+ htmlFor: "input-id"
128
+ }));
129
+ expect(asFragment()).toMatchSnapshot();
130
+ });
131
+ test('Render with i18nKeys', () => {
132
+ const {
133
+ asFragment
134
+ } = render( /*#__PURE__*/React.createElement(Label, {
135
+ text: "Label text",
136
+ isRequired: true,
137
+ requiredType: "text",
138
+ i18nKeys: {
139
+ requiredText: 'Custom Required'
140
+ }
141
+ }));
142
+ expect(asFragment()).toMatchSnapshot();
143
+ });
144
+ test('Render with customClass', () => {
145
+ const {
146
+ asFragment
147
+ } = render( /*#__PURE__*/React.createElement(Label, {
148
+ text: "Label text",
149
+ customClass: {
150
+ container: 'custom-container',
151
+ label: 'custom-label'
152
+ }
153
+ }));
154
+ expect(asFragment()).toMatchSnapshot();
155
+ });
156
+ test('Render with customProps', () => {
157
+ const {
158
+ asFragment
159
+ } = render( /*#__PURE__*/React.createElement(Label, {
160
+ text: "Label text",
161
+ customProps: {
162
+ container: {
163
+ $flag_shrink: true
164
+ },
165
+ label: {
166
+ $ui_lineClamp: '2'
167
+ }
168
+ }
169
+ }));
170
+ expect(asFragment()).toMatchSnapshot();
171
+ });
172
+ test('Render with customId and testId', () => {
173
+ const {
174
+ asFragment
175
+ } = render( /*#__PURE__*/React.createElement(Label, {
176
+ text: "Label text",
177
+ customId: "label-customId",
178
+ testId: "label-testId"
179
+ }));
180
+ expect(asFragment()).toMatchSnapshot();
181
+ });
182
+ test('Render with tag attributes', () => {
183
+ const {
184
+ asFragment
185
+ } = render( /*#__PURE__*/React.createElement(Label, {
186
+ text: "Label text",
187
+ tagAttributes: {
188
+ container: {
189
+ 'data-custom-attr': 'true'
190
+ },
191
+ label: {
192
+ 'data-custom-attr': 'true'
193
+ }
194
+ }
195
+ }));
196
+ expect(asFragment()).toMatchSnapshot();
197
+ });
198
+ test('Render with a11y attributes', () => {
199
+ const {
200
+ asFragment
201
+ } = render( /*#__PURE__*/React.createElement(Label, {
202
+ text: "Label text",
203
+ a11yAttributes: {
204
+ container: {
205
+ role: 'group'
206
+ },
207
+ label: {
208
+ 'aria-label': 'Custom label'
209
+ }
210
+ }
211
+ }));
212
+ expect(asFragment()).toMatchSnapshot();
213
+ });
214
+ });