@bspk/ui 1.1.17 → 1.1.19

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 (144) hide show
  1. package/dist/Avatar.d.ts +17 -6
  2. package/dist/Avatar.js +15 -6
  3. package/dist/Avatar.js.map +1 -1
  4. package/dist/AvatarGroup.d.ts +49 -0
  5. package/dist/AvatarGroup.js +18 -0
  6. package/dist/AvatarGroup.js.map +1 -0
  7. package/dist/Badge.js +1 -1
  8. package/dist/Button.d.ts +14 -4
  9. package/dist/Button.js +1 -1
  10. package/dist/Button.js.map +1 -1
  11. package/dist/Checkbox.d.ts +15 -2
  12. package/dist/Checkbox.js.map +1 -1
  13. package/dist/CheckboxGroup.d.ts +6 -3
  14. package/dist/CheckboxGroup.js.map +1 -1
  15. package/dist/CheckboxOption.d.ts +8 -1
  16. package/dist/CheckboxOption.js.map +1 -1
  17. package/dist/Chip.d.ts +3 -1
  18. package/dist/Chip.js.map +1 -1
  19. package/dist/Dialog.d.ts +3 -3
  20. package/dist/Dialog.js.map +1 -1
  21. package/dist/Divider.js +1 -1
  22. package/dist/Dropdown.d.ts +33 -7
  23. package/dist/Dropdown.js +5 -5
  24. package/dist/Dropdown.js.map +1 -1
  25. package/dist/ListItem.js +1 -1
  26. package/dist/Menu.d.ts +40 -21
  27. package/dist/Menu.js +63 -41
  28. package/dist/Menu.js.map +1 -1
  29. package/dist/NumberInput.d.ts +5 -1
  30. package/dist/NumberInput.js +7 -5
  31. package/dist/NumberInput.js.map +1 -1
  32. package/dist/Portal.d.ts +5 -1
  33. package/dist/Portal.js.map +1 -1
  34. package/dist/ProgressBar.d.ts +4 -0
  35. package/dist/ProgressBar.js.map +1 -1
  36. package/dist/ProgressionStepper.d.ts +9 -2
  37. package/dist/ProgressionStepper.js +1 -1
  38. package/dist/ProgressionStepper.js.map +1 -1
  39. package/dist/ProgressionStepperBar.d.ts +6 -0
  40. package/dist/ProgressionStepperBar.js.map +1 -1
  41. package/dist/Radio.d.ts +16 -2
  42. package/dist/Radio.js +2 -2
  43. package/dist/Radio.js.map +1 -1
  44. package/dist/RadioGroup.d.ts +26 -3
  45. package/dist/RadioGroup.js +3 -3
  46. package/dist/RadioGroup.js.map +1 -1
  47. package/dist/RadioOption.d.ts +9 -1
  48. package/dist/RadioOption.js.map +1 -1
  49. package/dist/SearchBar.d.ts +30 -4
  50. package/dist/SearchBar.js +16 -15
  51. package/dist/SearchBar.js.map +1 -1
  52. package/dist/SegmentedControl.d.ts +21 -2
  53. package/dist/SegmentedControl.js +1 -1
  54. package/dist/SegmentedControl.js.map +1 -1
  55. package/dist/Switch.d.ts +1 -1
  56. package/dist/SwitchGroup.d.ts +13 -6
  57. package/dist/SwitchGroup.js +1 -1
  58. package/dist/SwitchGroup.js.map +1 -1
  59. package/dist/TabGroup.d.ts +23 -5
  60. package/dist/TabGroup.js +1 -1
  61. package/dist/TabGroup.js.map +1 -1
  62. package/dist/Tag.d.ts +5 -2
  63. package/dist/Tag.js +1 -1
  64. package/dist/Tag.js.map +1 -1
  65. package/dist/TextInput.d.ts +15 -6
  66. package/dist/TextInput.js +11 -5
  67. package/dist/TextInput.js.map +1 -1
  68. package/dist/Textarea.d.ts +3 -3
  69. package/dist/avatar-group.css +1 -0
  70. package/dist/avatar.css +1 -1
  71. package/dist/badge.css +1 -1
  72. package/dist/button.css +1 -1
  73. package/dist/demo/ExampleModalRender.d.ts +7 -0
  74. package/dist/demo/ExampleModalRender.js +16 -0
  75. package/dist/demo/ExampleModalRender.js.map +1 -0
  76. package/dist/demo/ExamplePlaceholder.d.ts +7 -0
  77. package/dist/demo/ExamplePlaceholder.js +13 -0
  78. package/dist/demo/ExamplePlaceholder.js.map +1 -0
  79. package/dist/demo/examples.d.ts +101 -0
  80. package/dist/demo/examples.js +483 -0
  81. package/dist/demo/examples.js.map +1 -0
  82. package/dist/divider.css +1 -1
  83. package/dist/dropdown.css +1 -1
  84. package/dist/hooks/useKeyboardNavigation.js +5 -2
  85. package/dist/hooks/useKeyboardNavigation.js.map +1 -1
  86. package/dist/hooks/useOptionIconsInvalid.d.ts +10 -1
  87. package/dist/hooks/useOptionIconsInvalid.js.map +1 -1
  88. package/dist/index.d.ts +4 -26
  89. package/dist/index.js.map +1 -1
  90. package/dist/list-item.css +1 -1
  91. package/dist/menu.css +1 -1
  92. package/dist/segmented-control.css +1 -1
  93. package/dist/tab-group.css +1 -1
  94. package/dist/tag.css +1 -1
  95. package/dist/text-input.css +1 -1
  96. package/dist/utils/children.js.map +1 -1
  97. package/meta-types.ts +2 -0
  98. package/meta.ts +76 -42
  99. package/package.json +1 -1
  100. package/src/Avatar.tsx +35 -8
  101. package/src/AvatarGroup.tsx +71 -0
  102. package/src/Button.tsx +14 -4
  103. package/src/Checkbox.tsx +25 -11
  104. package/src/CheckboxGroup.tsx +6 -3
  105. package/src/CheckboxOption.tsx +9 -2
  106. package/src/Chip.tsx +3 -1
  107. package/src/Dialog.tsx +3 -3
  108. package/src/Dropdown.tsx +40 -11
  109. package/src/Menu.tsx +159 -108
  110. package/src/NumberInput.tsx +15 -6
  111. package/src/Portal.tsx +5 -1
  112. package/src/ProgressBar.tsx +4 -0
  113. package/src/ProgressionStepper.tsx +9 -2
  114. package/src/ProgressionStepperBar.tsx +6 -0
  115. package/src/Radio.tsx +21 -4
  116. package/src/RadioGroup.tsx +34 -6
  117. package/src/RadioOption.tsx +11 -2
  118. package/src/SearchBar.tsx +87 -44
  119. package/src/SegmentedControl.tsx +21 -2
  120. package/src/Switch.tsx +1 -1
  121. package/src/SwitchGroup.tsx +19 -7
  122. package/src/TabGroup.tsx +23 -5
  123. package/src/Tag.tsx +5 -2
  124. package/src/TextInput.tsx +25 -15
  125. package/src/Textarea.tsx +3 -3
  126. package/src/avatar-group.scss +17 -0
  127. package/src/avatar.scss +26 -2
  128. package/src/badge.scss +1 -0
  129. package/src/button.scss +1 -0
  130. package/src/demo/ExampleModalRender.tsx +37 -0
  131. package/src/demo/ExamplePlaceholder.tsx +40 -0
  132. package/src/demo/examples.tsx +699 -0
  133. package/src/divider.scss +2 -0
  134. package/src/dropdown.scss +5 -0
  135. package/src/hooks/useKeyboardNavigation.ts +3 -2
  136. package/src/hooks/useOptionIconsInvalid.ts +10 -1
  137. package/src/index.ts +5 -32
  138. package/src/list-item.scss +5 -1
  139. package/src/menu.scss +5 -1
  140. package/src/segmented-control.scss +1 -0
  141. package/src/tab-group.scss +1 -0
  142. package/src/tag.scss +1 -0
  143. package/src/text-input.scss +13 -18
  144. package/src/utils/children.ts +1 -1
package/src/Menu.tsx CHANGED
@@ -1,12 +1,15 @@
1
1
  import './menu.scss';
2
- import { ComponentProps, CSSProperties, MouseEvent as ReactMouseEvent, useMemo } from 'react';
2
+ import { ComponentProps, CSSProperties, ReactNode, useMemo } from 'react';
3
3
 
4
4
  import { Checkbox } from './Checkbox';
5
5
  import { ListItem } from './ListItem';
6
- import { Txt } from './Txt';
7
6
  import { useId } from './hooks/useId';
8
7
 
9
- import { CommonProps, ElementProps } from './';
8
+ import { CommonProps, ElementProps, SetRef } from './';
9
+
10
+ const DEFAULT = {
11
+ selectAll: 'Select All',
12
+ };
10
13
 
11
14
  export const MIN_ITEM_COUNT = 3;
12
15
  export const MAX_ITEM_COUNT = 10;
@@ -16,12 +19,12 @@ export function menuItemId(menuId: string, index: number) {
16
19
  }
17
20
 
18
21
  /** The props for the renderListItem function. Useful for customizing menu list items. */
19
- export type RenderListItemParams<M extends MenuItem> = Pick<
20
- MenuProps<M>,
22
+ export type RenderListItemParams<T extends MenuItem = MenuItem> = Pick<
23
+ MenuProps<T>,
21
24
  'activeIndex' | 'isMulti' | 'selectedValues'
22
25
  > & {
23
26
  index: number;
24
- item: M;
27
+ item: T;
25
28
  menuId: string;
26
29
  selected: boolean;
27
30
  itemId?: string;
@@ -37,7 +40,7 @@ export type MenuItem = CommonProps<'disabled'> & {
37
40
  id?: string;
38
41
  };
39
42
 
40
- export type MenuProps<Item extends MenuItem = MenuItem> = CommonProps<'disabled' | 'id'> & {
43
+ export type MenuProps<T extends MenuItem = MenuItem> = CommonProps<'disabled' | 'id'> & {
41
44
  /**
42
45
  * The number of items to display in the menu
43
46
  *
@@ -49,17 +52,27 @@ export type MenuProps<Item extends MenuItem = MenuItem> = CommonProps<'disabled'
49
52
  /**
50
53
  * Content to display in the menu.
51
54
  *
52
- * @type MenuItems
53
- */
54
- items?: Item[];
55
- /** A ref to the inner div element. */
56
- innerRef?: (node: HTMLElement | null) => void;
57
- /**
58
- * Message to display when no results are found
55
+ * @example
56
+ * [
57
+ * { value: '1', label: 'Option 1' },
58
+ * { value: '2', label: 'Option 2' },
59
+ * { value: '3', label: 'Option 3' },
60
+ * { value: '4', label: 'Option 4' },
61
+ * { value: '5', label: 'Option 5' },
62
+ * { value: '6', label: 'Option 6' },
63
+ * { value: '7', label: 'Option 7' },
64
+ * { value: '8', label: 'Option 8' },
65
+ * { value: '9', label: 'Option 9' },
66
+ * { value: '10', label: 'Option 10' },
67
+ * ];
59
68
  *
60
- * @type multiline
69
+ * @type Array<MenuItem>
61
70
  */
62
- noResultsMessage?: string;
71
+ items?: T[];
72
+ /** A ref to the inner div element. */
73
+ innerRef?: SetRef<HTMLDivElement>;
74
+ /** Message to display when no results are found */
75
+ noResultsMessage?: ReactNode;
63
76
  /** The index of the currently highlighted item. */
64
77
  activeIndex?: number;
65
78
  /** The values of the selected items */
@@ -71,22 +84,31 @@ export type MenuProps<Item extends MenuItem = MenuItem> = CommonProps<'disabled'
71
84
  * @param {RenderListItemParams} props
72
85
  * @returns {ComponentProps<typeof ListItem>}
73
86
  */
74
- renderListItem?: (props: RenderListItemParams<Item>) => Partial<ComponentProps<typeof ListItem>>;
87
+ renderListItem?: (props: RenderListItemParams<T>) => Partial<ComponentProps<typeof ListItem>>;
75
88
  /**
76
89
  * Whether the menu allows multiple selections.
77
90
  *
78
91
  * @default false
79
92
  */
80
93
  isMulti?: boolean;
94
+ /**
95
+ * The label for the "Select All" option.
96
+ *
97
+ * Ignored if `isMulti` is false.
98
+ *
99
+ * If `isMulti` is `true`, defaults to "Select All". If a string, it will be used as the label. If false the select
100
+ * all option will not be rendered.
101
+ *
102
+ * @default false
103
+ */
104
+ selectAll?: boolean | string;
81
105
  /**
82
106
  * The function to call when the selected values change.
83
107
  *
84
- * @type (selectedValues: String[], event: ChangeEvent) => void
85
- * @param {string[]} selectedValues
86
- * @param {ChangeEvent} event
87
- * @returns {void}
108
+ * @example
109
+ * (selectedValues, event) => setState({ selectedValues });
88
110
  */
89
- onChange?: (selectedValues: string[], event?: ReactMouseEvent<HTMLButtonElement, MouseEvent>) => void;
111
+ onChange?: (selectedValues: string[], event?: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
90
112
  };
91
113
 
92
114
  /**
@@ -94,7 +116,7 @@ export type MenuProps<Item extends MenuItem = MenuItem> = CommonProps<'disabled'
94
116
  *
95
117
  * @name Menu
96
118
  */
97
- function Menu<Item extends MenuItem = MenuItem>({
119
+ function Menu({
98
120
  itemCount: itemCountProp = 5,
99
121
  items: itemsProp = [],
100
122
  noResultsMessage,
@@ -106,24 +128,39 @@ function Menu<Item extends MenuItem = MenuItem>({
106
128
  id: idProp,
107
129
  renderListItem,
108
130
  isMulti,
131
+ selectAll: selectAllProp,
109
132
  ...props
110
- }: ElementProps<MenuProps<Item>, 'div'>) {
133
+ }: ElementProps<MenuProps, 'div'>) {
111
134
  const menuId = useId(idProp);
112
- const items = Array.isArray(itemsProp) ? itemsProp : [];
113
- const itemCount = useMemo(
114
- () =>
135
+
136
+ const selectAll = useMemo(() => {
137
+ if (!isMulti) return false;
138
+ if (selectAllProp && typeof selectAllProp === 'string') return selectAllProp;
139
+ return selectAllProp === true ? DEFAULT.selectAll : false;
140
+ }, [isMulti, selectAllProp]);
141
+
142
+ const { items, itemCount } = useMemo(() => {
143
+ const itemsNext = Array.isArray(itemsProp) ? itemsProp : [];
144
+ return {
145
+ items: itemsNext,
115
146
  // Ensure itemCount is within the range of items.length
116
- Math.min(
117
- items.length,
147
+ itemCount: Math.min(
148
+ itemsNext.length,
118
149
  // pin itemCountProp to a range of 3 to 10
119
150
  Math.max(MIN_ITEM_COUNT, Math.min(itemCountProp, MAX_ITEM_COUNT)),
120
151
  ),
121
- [itemCountProp, items.length],
152
+ };
153
+ }, [itemCountProp, itemsProp]);
154
+
155
+ const allSelected = useMemo(
156
+ () => !!(items.length && items.every((item) => selectedValues.includes(item.value))),
157
+ [items, selectedValues],
122
158
  );
123
159
 
124
160
  return (
125
161
  <div
126
162
  {...props}
163
+ aria-multiselectable={isMulti || undefined}
127
164
  data-bspk="menu"
128
165
  data-disabled={disabled || undefined}
129
166
  data-item-count={itemCount || undefined}
@@ -133,87 +170,101 @@ function Menu<Item extends MenuItem = MenuItem>({
133
170
  role="listbox"
134
171
  style={{ ...props.style, '--item-count': itemCount } as CSSProperties}
135
172
  >
136
- {items.length ? (
137
- items.map((item, index) => {
138
- const itemId = item.id || menuItemId(menuId, index);
139
-
140
- const selected = Array.isArray(selectedValues) && selectedValues.includes(item.value);
141
-
142
- const renderProps = renderListItem?.({
143
- activeIndex,
144
- index,
145
- item,
146
- selectedValues,
147
- isMulti,
148
- menuId: menuId || '',
149
- selected,
150
- itemId,
151
- });
152
-
153
- return (
154
- <ListItem
155
- {...renderProps}
156
- active={activeIndex === index || undefined}
157
- aria-disabled={item.disabled || undefined}
158
- aria-posinset={index + 1}
159
- aria-selected={selected || undefined}
160
- as="button"
161
- data-menu-item
162
- data-selected={selected || undefined}
163
- disabled={item.disabled || undefined}
164
- id={itemId}
165
- key={itemId}
166
- label={renderProps?.label?.toString() || item.label?.toString()}
167
- onClick={(event: ReactMouseEvent<HTMLButtonElement, MouseEvent>) => {
168
- if (renderProps) renderProps?.onClick?.(event);
169
-
170
- if (onChange) {
171
- if (!isMulti) {
172
- onChange?.([item.value], event);
173
- return;
174
- }
175
- onChange(
176
- selected
177
- ? selectedValues.filter((value) => value !== item.value)
178
- : [...selectedValues, item.value],
179
- event,
180
- );
181
- }
173
+ {isMulti && selectAll && (
174
+ <ListItem
175
+ as="button"
176
+ data-selected={allSelected || undefined}
177
+ key="select-all"
178
+ label={selectAll}
179
+ onClick={(event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
180
+ onChange?.(allSelected ? [] : items.map((item) => item.value), event);
181
+ }}
182
+ role="option"
183
+ tabIndex={-1}
184
+ trailing={
185
+ <Checkbox
186
+ aria-label={selectAll}
187
+ checked={!!allSelected}
188
+ name=""
189
+ onChange={(checked) => {
190
+ onChange?.(checked ? items.map((item) => item.value) : []);
182
191
  }}
183
- role="option"
184
- tabIndex={-1}
185
- trailing={
186
- isMulti ? (
187
- <Checkbox
188
- aria-label={item.label}
189
- checked={selected}
190
- name={item.value}
191
- onChange={(checked) => {
192
- onChange?.(
193
- checked
194
- ? selectedValues.filter((value) => value !== item.value)
195
- : [...selectedValues, item.value],
196
- );
197
- }}
198
- value={item.value}
199
- />
200
- ) : (
201
- renderProps?.trailing
202
- )
203
- }
192
+ value=""
204
193
  />
205
- );
206
- })
207
- ) : (
208
- <>
209
- <Txt as="div" variant="heading-h5">
210
- No results found
211
- </Txt>
212
- <Txt as="div" variant="body-base">
213
- {noResultsMessage}
214
- </Txt>
215
- </>
194
+ }
195
+ />
216
196
  )}
197
+ {items.length
198
+ ? items.map((item, index) => {
199
+ const itemId = item.id || menuItemId(menuId, index);
200
+
201
+ const selected = Boolean(Array.isArray(selectedValues) && selectedValues.includes(item.value));
202
+
203
+ const renderProps = renderListItem?.({
204
+ activeIndex,
205
+ index,
206
+ item,
207
+ selectedValues,
208
+ isMulti,
209
+ menuId: menuId || '',
210
+ selected,
211
+ itemId,
212
+ });
213
+
214
+ return (
215
+ <ListItem
216
+ {...renderProps}
217
+ active={activeIndex === index || undefined}
218
+ aria-disabled={item.disabled || undefined}
219
+ aria-posinset={index + 1}
220
+ aria-selected={selected || undefined}
221
+ as="button"
222
+ //data-selected={selected || undefined}
223
+ disabled={item.disabled || undefined}
224
+ id={itemId}
225
+ key={itemId}
226
+ label={renderProps?.label?.toString() || item.label?.toString()}
227
+ onClick={(event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
228
+ if (renderProps) renderProps?.onClick?.(event);
229
+
230
+ if (onChange) {
231
+ if (!isMulti) {
232
+ onChange?.([item.value], event);
233
+ return;
234
+ }
235
+ onChange(
236
+ selected
237
+ ? selectedValues.filter((value) => value !== item.value)
238
+ : [...selectedValues, item.value],
239
+ event,
240
+ );
241
+ }
242
+ }}
243
+ role="option"
244
+ tabIndex={-1}
245
+ trailing={
246
+ isMulti ? (
247
+ <Checkbox
248
+ aria-label={item.label}
249
+ checked={selected}
250
+ name={item.value}
251
+ onChange={(checked) => {
252
+ onChange?.(
253
+ checked
254
+ ? selectedValues.filter((value) => value !== item.value)
255
+ : [...selectedValues, item.value],
256
+ );
257
+ }}
258
+ value={item.value}
259
+ />
260
+ ) : (
261
+ renderProps?.trailing
262
+ )
263
+ }
264
+ />
265
+ );
266
+ })
267
+ : noResultsMessage}
217
268
  </div>
218
269
  );
219
270
  }
@@ -25,7 +25,11 @@ export type NumberInputProps = CommonProps<'aria-label' | 'disabled' | 'id' | 'n
25
25
  InvalidPropsLibrary & {
26
26
  /** The value of the control. */
27
27
  value?: number;
28
- /** Callback when the value changes. */
28
+ /**
29
+ * Callback when the value changes.
30
+ *
31
+ * @required
32
+ */
29
33
  onChange: (value: number | undefined) => void;
30
34
  /**
31
35
  * The alignment of the input box. Centered between the plus and minus buttons or to the left of the buttons.
@@ -78,8 +82,10 @@ function NumberInput({
78
82
  const max = isNumber(maxProp);
79
83
  const min = isNumber(minProp);
80
84
 
81
- const fix = (next: number | undefined = value) => {
82
- if (typeof next !== 'number') {
85
+ const fix = (nextValue: number | string | undefined) => {
86
+ const next = isNumber(nextValue);
87
+
88
+ if (typeof next !== 'number' || isNaN(next)) {
83
89
  onChange(undefined);
84
90
  return;
85
91
  }
@@ -116,12 +122,15 @@ function NumberInput({
116
122
  aria-label={ariaLabel}
117
123
  disabled={disabled}
118
124
  id={inputId}
125
+ max={max}
126
+ min={min}
119
127
  name={name}
120
- onBlur={() => {
121
- fix();
128
+ onBlur={(event) => {
129
+ // Fix the value on blur to ensure it is a valid number
130
+ fix(event.target.value);
122
131
  }}
123
132
  onChange={(event) => {
124
- onChange(event.target.value as unknown as number);
133
+ onChange(isNumber(event.target.value));
125
134
  }}
126
135
  readOnly={readOnly}
127
136
  type="number"
package/src/Portal.tsx CHANGED
@@ -8,7 +8,11 @@ export type PortalProps = {
8
8
  * @required
9
9
  */
10
10
  children: ReactNode;
11
- /** The container to render the portal in. */
11
+ /**
12
+ * The container to render the portal in.
13
+ *
14
+ * @optional
15
+ */
12
16
  container?: HTMLElement;
13
17
  };
14
18
 
@@ -11,9 +11,13 @@ export type ProgressBarProps = {
11
11
  /**
12
12
  * The current progress of the progressbar.
13
13
  *
14
+ * @example
15
+ * 42;
16
+ *
14
17
  * @default 0
15
18
  * @minimum 0
16
19
  * @maximum 100
20
+ *
17
21
  * @required
18
22
  */
19
23
  completion: number;
@@ -30,7 +30,14 @@ export type ProgressionStepperProps = {
30
30
  /**
31
31
  * The steps to display in the progress bar.
32
32
  *
33
- * @type ProgressionStepperItem[]
33
+ * @example
34
+ * [
35
+ * { name: '[Name of step to proceed forward 1]' },
36
+ * { name: '[Name of step to proceed forward 2]' },
37
+ * { name: '[Name of step to proceed forward 3]' },
38
+ * ];
39
+ *
40
+ * @type Array<ProgressionStepperItem>
34
41
  * @required
35
42
  */
36
43
  steps: ProgressionStepperItem[];
@@ -67,7 +74,7 @@ function ProgressionStepper({
67
74
  ...containerProps
68
75
  }: ElementProps<ProgressionStepperProps, 'div'>) {
69
76
  const currentStep = Math.max(0, Math.min(currentStepProp, steps.length + 1));
70
- return (
77
+ return !steps?.length ? null : (
71
78
  <div {...containerProps} data-bspk="progression-stepper" data-variant={variant}>
72
79
  {variant === 'widget' && (
73
80
  <label>
@@ -5,6 +5,9 @@ export type ProgressionStepperBarProps = {
5
5
  /**
6
6
  * The number of steps in the progress bar.
7
7
  *
8
+ * @example
9
+ * 5;
10
+ *
8
11
  * @minimum 2
9
12
  * @maximum 10
10
13
  * @required
@@ -13,6 +16,9 @@ export type ProgressionStepperBarProps = {
13
16
  /**
14
17
  * The last step completed.
15
18
  *
19
+ * @example
20
+ * 2;
21
+ *
16
22
  * @default 0
17
23
  * @minimum 0
18
24
  */
package/src/Radio.tsx CHANGED
@@ -1,8 +1,24 @@
1
1
  import './radio.scss';
2
- import { ToggleControlProps, ElementProps, InvalidPropsLibrary } from './';
2
+ import { ChangeEvent } from 'react';
3
3
 
4
- export type RadioProps = InvalidPropsLibrary &
5
- Pick<ToggleControlProps<HTMLInputElement>, 'aria-label' | 'checked' | 'disabled' | 'name' | 'onChange' | 'value'>;
4
+ import { ElementProps, InvalidPropsLibrary, CommonProps } from './';
5
+
6
+ export type RadioProps = CommonProps<'aria-label' | 'disabled' | 'name'> &
7
+ InvalidPropsLibrary &
8
+ Required<CommonProps<'value'>> & {
9
+ /**
10
+ * Marks the radio as checked.
11
+ *
12
+ * @default false
13
+ */
14
+ checked?: boolean;
15
+ /**
16
+ * The function to call when the radio is checked.
17
+ *
18
+ * @required
19
+ */
20
+ onChange: (checked: boolean, event: ChangeEvent<HTMLInputElement>) => void;
21
+ };
6
22
 
7
23
  /**
8
24
  * A round control that allows user to choose one option from a set. This is the base element and if used directly you
@@ -13,13 +29,14 @@ export type RadioProps = InvalidPropsLibrary &
13
29
  * @name Radio
14
30
  */
15
31
  function Radio(props: ElementProps<RadioProps, 'input'>) {
16
- const { checked = false, invalid, disabled, onChange, ...otherProps } = props;
32
+ const { checked = false, invalid, disabled, onChange, errorMessage, ...otherProps } = props;
17
33
 
18
34
  return (
19
35
  <span data-bspk="radio">
20
36
  <input
21
37
  {...otherProps}
22
38
  checked={!!checked}
39
+ data-errormessage={errorMessage || undefined}
23
40
  data-invalid={invalid || undefined}
24
41
  disabled={disabled || undefined}
25
42
  onChange={(event) => onChange(!!event.target.checked, event)}
@@ -5,21 +5,44 @@ import { ElementProps, CommonProps } from './';
5
5
 
6
6
  export type RadioGroupOption = Pick<ToggleOptionProps, 'description' | 'label'> & Required<CommonProps<'value'>>;
7
7
 
8
- export type RadioGroupProps = CommonProps<'name' | 'value'> & {
8
+ export type RadioGroupProps = CommonProps<'name'> & {
9
+ /**
10
+ * The value of the control.
11
+ *
12
+ * @example
13
+ * 1;
14
+ *
15
+ * @required
16
+ */
17
+ value: string;
9
18
  /**
10
19
  * The function to call when the radios are changed.
11
20
  *
21
+ * @example
22
+ * (value) => setState({ value }),
23
+ *
12
24
  * @required
13
25
  */
14
26
  onChange: (value: string) => void;
15
27
  /**
16
28
  * The options for the radios.
17
29
  *
18
- * @type RadioGroupOption[]
30
+ * @example
31
+ * [
32
+ * { value: '1', label: 'Option 1', description: 'Description here' },
33
+ * { value: '2', label: 'Option 2' },
34
+ * { value: '3', label: 'Option 3' },
35
+ * ];
36
+ *
37
+ * @type Array<RadioGroupOption>
19
38
  * @required
20
39
  */
21
40
  options: RadioGroupOption[];
22
- /** The size of the radio group labels. */
41
+ /**
42
+ * The size of the radio group labels.
43
+ *
44
+ * @default base
45
+ */
23
46
  size?: 'base' | 'large' | 'small';
24
47
  };
25
48
 
@@ -33,14 +56,19 @@ function RadioGroup({
33
56
  options = [],
34
57
  name,
35
58
  value: groupValue,
36
- size,
59
+ size = 'base',
37
60
  ...props
38
61
  }: ElementProps<RadioGroupProps, 'div'>) {
39
62
  return (
40
63
  <div {...props} data-bspk="radio-group" role="radiogroup" style={{ display: 'contents' }}>
41
- {options.map(({ label, description, value }) => {
64
+ {options.map(({ label, description, value }, index) => {
42
65
  return (
43
- <ToggleOption description={description} key={value} label={label} size={size}>
66
+ <ToggleOption
67
+ description={description}
68
+ key={`toggle-option-${value || index}`}
69
+ label={label}
70
+ size={size}
71
+ >
44
72
  <Radio
45
73
  aria-label={label}
46
74
  checked={groupValue === value}
@@ -1,11 +1,20 @@
1
+ import { ChangeEvent } from 'react';
2
+
1
3
  import { RadioProps, Radio } from './Radio';
2
4
  import { ToggleOptionProps, ToggleOption } from './ToggleOption';
3
5
 
4
6
  import { InvalidPropsLibrary } from '.';
5
7
 
6
8
  export type RadioOptionProps = InvalidPropsLibrary &
7
- Pick<RadioProps, 'checked' | 'disabled' | 'name' | 'onChange' | 'value'> &
8
- Pick<ToggleOptionProps, 'description' | 'label'>;
9
+ Pick<RadioProps, 'checked' | 'disabled' | 'name' | 'value'> &
10
+ Pick<ToggleOptionProps, 'description' | 'label'> & {
11
+ /**
12
+ * The function to call when the radio is checked.
13
+ *
14
+ * @required
15
+ */
16
+ onChange: (checked: boolean, event: ChangeEvent<HTMLInputElement>) => void;
17
+ };
9
18
 
10
19
  /**
11
20
  * A control that allows users to choose one or more items from a list or turn an feature on or off.