@transferwise/components 46.111.0 → 46.112.0
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/build/avatarLayout/AvatarLayout.js.map +1 -1
- package/build/avatarLayout/AvatarLayout.mjs.map +1 -1
- package/build/avatarView/AvatarView.js +27 -29
- package/build/avatarView/AvatarView.js.map +1 -1
- package/build/avatarView/AvatarView.mjs +27 -29
- package/build/avatarView/AvatarView.mjs.map +1 -1
- package/build/avatarView/{NotificationDot.js → Dot.js} +14 -12
- package/build/avatarView/Dot.js.map +1 -0
- package/build/avatarView/{NotificationDot.mjs → Dot.mjs} +14 -12
- package/build/avatarView/Dot.mjs.map +1 -0
- package/build/badge/BadgeAssets.js.map +1 -1
- package/build/badge/BadgeAssets.mjs.map +1 -1
- package/build/common/panel/Panel.js +1 -0
- package/build/common/panel/Panel.js.map +1 -1
- package/build/common/panel/Panel.mjs +1 -0
- package/build/common/panel/Panel.mjs.map +1 -1
- package/build/common/responsivePanel/ResponsivePanel.js +6 -1
- package/build/common/responsivePanel/ResponsivePanel.js.map +1 -1
- package/build/common/responsivePanel/ResponsivePanel.mjs +6 -1
- package/build/common/responsivePanel/ResponsivePanel.mjs.map +1 -1
- package/build/dateInput/DateInput.js +46 -24
- package/build/dateInput/DateInput.js.map +1 -1
- package/build/dateInput/DateInput.mjs +48 -26
- package/build/dateInput/DateInput.mjs.map +1 -1
- package/build/dateLookup/DateLookup.js +5 -2
- package/build/dateLookup/DateLookup.js.map +1 -1
- package/build/dateLookup/DateLookup.mjs +5 -2
- package/build/dateLookup/DateLookup.mjs.map +1 -1
- package/build/dateLookup/dateTrigger/DateTrigger.js +2 -0
- package/build/dateLookup/dateTrigger/DateTrigger.js.map +1 -1
- package/build/dateLookup/dateTrigger/DateTrigger.mjs +2 -0
- package/build/dateLookup/dateTrigger/DateTrigger.mjs.map +1 -1
- package/build/field/Field.js +7 -2
- package/build/field/Field.js.map +1 -1
- package/build/field/Field.mjs +13 -8
- package/build/field/Field.mjs.map +1 -1
- package/build/inputs/InputGroup.js +1 -1
- package/build/inputs/InputGroup.js.map +1 -1
- package/build/inputs/InputGroup.mjs +2 -2
- package/build/inputs/InputGroup.mjs.map +1 -1
- package/build/inputs/SelectInput.js +54 -5
- package/build/inputs/SelectInput.js.map +1 -1
- package/build/inputs/SelectInput.mjs +54 -5
- package/build/inputs/SelectInput.mjs.map +1 -1
- package/build/inputs/contexts.js +8 -4
- package/build/inputs/contexts.js.map +1 -1
- package/build/inputs/contexts.mjs +7 -4
- package/build/inputs/contexts.mjs.map +1 -1
- package/build/label/Label.js +14 -8
- package/build/label/Label.js.map +1 -1
- package/build/label/Label.mjs +14 -8
- package/build/label/Label.mjs.map +1 -1
- package/build/listItem/Prompt/ListItemPrompt.js +1 -1
- package/build/listItem/Prompt/ListItemPrompt.js.map +1 -1
- package/build/listItem/Prompt/ListItemPrompt.mjs +1 -1
- package/build/listItem/Prompt/ListItemPrompt.mjs.map +1 -1
- package/build/main.css +180 -164
- package/build/moneyInput/MoneyInput.js +6 -5
- package/build/moneyInput/MoneyInput.js.map +1 -1
- package/build/moneyInput/MoneyInput.mjs +6 -5
- package/build/moneyInput/MoneyInput.mjs.map +1 -1
- package/build/phoneNumberInput/PhoneNumberInput.js +25 -3
- package/build/phoneNumberInput/PhoneNumberInput.js.map +1 -1
- package/build/phoneNumberInput/PhoneNumberInput.mjs +27 -5
- package/build/phoneNumberInput/PhoneNumberInput.mjs.map +1 -1
- package/build/{listItem/Prompt → prompt}/InlinePrompt/InlinePrompt.js +23 -23
- package/build/prompt/InlinePrompt/InlinePrompt.js.map +1 -0
- package/build/{listItem/Prompt → prompt}/InlinePrompt/InlinePrompt.mjs +23 -23
- package/build/prompt/InlinePrompt/InlinePrompt.mjs.map +1 -0
- package/build/styles/avatarView/AvatarView.css +17 -11
- package/build/styles/avatarView/Dot.css +26 -0
- package/build/styles/inputs/Input.css +5 -0
- package/build/styles/inputs/TextArea.css +5 -0
- package/build/styles/listItem/ListItem.css +5 -153
- package/build/styles/listItem/Prompt/ListItemPrompt.css +0 -153
- package/build/styles/main.css +180 -164
- package/build/types/avatarLayout/AvatarLayout.d.ts +1 -1
- package/build/types/avatarLayout/AvatarLayout.d.ts.map +1 -1
- package/build/types/avatarView/AvatarView.d.ts +1 -2
- package/build/types/avatarView/AvatarView.d.ts.map +1 -1
- package/build/types/avatarView/Dot.d.ts +8 -0
- package/build/types/avatarView/Dot.d.ts.map +1 -0
- package/build/types/badge/BadgeAssets.d.ts +1 -1
- package/build/types/badge/BadgeAssets.d.ts.map +1 -1
- package/build/types/common/panel/Panel.d.ts +2 -0
- package/build/types/common/panel/Panel.d.ts.map +1 -1
- package/build/types/common/responsivePanel/ResponsivePanel.d.ts +1 -0
- package/build/types/common/responsivePanel/ResponsivePanel.d.ts.map +1 -1
- package/build/types/dateInput/DateInput.d.ts +2 -2
- package/build/types/dateInput/DateInput.d.ts.map +1 -1
- package/build/types/dateLookup/DateLookup.d.ts.map +1 -1
- package/build/types/dateLookup/dateTrigger/DateTrigger.d.ts +1 -0
- package/build/types/dateLookup/dateTrigger/DateTrigger.d.ts.map +1 -1
- package/build/types/field/Field.d.ts.map +1 -1
- package/build/types/inputs/InputGroup.d.ts.map +1 -1
- package/build/types/inputs/SelectInput.d.ts +27 -1
- package/build/types/inputs/SelectInput.d.ts.map +1 -1
- package/build/types/inputs/contexts.d.ts +6 -1
- package/build/types/inputs/contexts.d.ts.map +1 -1
- package/build/types/label/Label.d.ts +5 -15
- package/build/types/label/Label.d.ts.map +1 -1
- package/build/types/listItem/Prompt/ListItemPrompt.d.ts +1 -1
- package/build/types/listItem/Prompt/ListItemPrompt.d.ts.map +1 -1
- package/build/types/moneyInput/MoneyInput.d.ts.map +1 -1
- package/build/types/phoneNumberInput/PhoneNumberInput.d.ts.map +1 -1
- package/build/types/{listItem/Prompt → prompt}/InlinePrompt/InlinePrompt.d.ts +1 -1
- package/build/types/prompt/InlinePrompt/InlinePrompt.d.ts.map +1 -0
- package/build/types/prompt/InlinePrompt/index.d.ts.map +1 -0
- package/build/types/prompt/index.d.ts +3 -0
- package/build/types/prompt/index.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/DisabledComponents.story.tsx +156 -0
- package/src/avatarLayout/AvatarLayout.tsx +1 -1
- package/src/avatarView/AvatarView.css +17 -11
- package/src/avatarView/AvatarView.less +1 -1
- package/src/avatarView/AvatarView.story.tsx +92 -36
- package/src/avatarView/AvatarView.tsx +35 -30
- package/src/avatarView/Dot.css +26 -0
- package/src/avatarView/Dot.less +31 -0
- package/src/avatarView/Dot.tsx +42 -0
- package/src/badge/BadgeAssets.tsx +1 -1
- package/src/common/panel/Panel.tsx +2 -0
- package/src/common/responsivePanel/ResponsivePanel.tsx +7 -1
- package/src/dateInput/DateInput.spec.tsx +45 -7
- package/src/dateInput/DateInput.story.tsx +2 -0
- package/src/dateInput/DateInput.tsx +65 -30
- package/src/dateLookup/DateLookup.spec.tsx +16 -0
- package/src/dateLookup/DateLookup.tsx +6 -3
- package/src/dateLookup/dateTrigger/DateTrigger.tsx +3 -0
- package/src/field/Field.tsx +6 -5
- package/src/inputs/Input.css +5 -0
- package/src/inputs/InputGroup.tsx +3 -4
- package/src/inputs/SelectInput.story.tsx +101 -0
- package/src/inputs/SelectInput.tsx +113 -5
- package/src/inputs/TextArea.css +5 -0
- package/src/inputs/_common.less +5 -0
- package/src/inputs/contexts.tsx +12 -3
- package/src/label/Label.tsx +26 -20
- package/src/listItem/AvatarView/ListItemAvatarView.story.tsx +89 -25
- package/src/listItem/ListItem.css +5 -153
- package/src/listItem/ListItem.less +5 -0
- package/src/listItem/Prompt/ListItemPrompt.css +0 -153
- package/src/listItem/Prompt/ListItemPrompt.less +0 -2
- package/src/listItem/Prompt/ListItemPrompt.tsx +1 -1
- package/src/main.css +180 -164
- package/src/main.less +1 -0
- package/src/moneyInput/MoneyInput.spec.tsx +16 -1
- package/src/moneyInput/MoneyInput.tsx +7 -6
- package/src/phoneNumberInput/PhoneNumberInput.spec.tsx +32 -0
- package/src/phoneNumberInput/PhoneNumberInput.tsx +32 -11
- package/src/{listItem/Prompt → prompt}/InlinePrompt/InlinePrompt.spec.tsx +2 -2
- package/src/{listItem/Prompt → prompt}/InlinePrompt/InlinePrompt.tsx +4 -4
- package/src/prompt/index.ts +6 -0
- package/build/avatarView/NotificationDot.js.map +0 -1
- package/build/avatarView/NotificationDot.mjs.map +0 -1
- package/build/listItem/Prompt/InlinePrompt/InlinePrompt.js.map +0 -1
- package/build/listItem/Prompt/InlinePrompt/InlinePrompt.mjs.map +0 -1
- package/build/styles/avatarView/NotificationDot.css +0 -20
- package/build/types/avatarView/NotificationDot.d.ts +0 -8
- package/build/types/avatarView/NotificationDot.d.ts.map +0 -1
- package/build/types/listItem/Prompt/InlinePrompt/InlinePrompt.d.ts.map +0 -1
- package/build/types/listItem/Prompt/InlinePrompt/index.d.ts.map +0 -1
- package/src/avatarView/NotificationDot.css +0 -20
- package/src/avatarView/NotificationDot.less +0 -24
- package/src/avatarView/NotificationDot.tsx +0 -35
- /package/build/styles/{listItem/Prompt → prompt}/InlinePrompt/InlinePrompt.css +0 -0
- /package/build/types/{listItem/Prompt → prompt}/InlinePrompt/index.d.ts +0 -0
- /package/src/{listItem/Prompt → prompt}/InlinePrompt/InlinePrompt.css +0 -0
- /package/src/{listItem/Prompt → prompt}/InlinePrompt/InlinePrompt.less +0 -0
- /package/src/{listItem/Prompt → prompt}/InlinePrompt/index.ts +0 -0
|
@@ -146,10 +146,34 @@ function filterSelectInputItems<T>(
|
|
|
146
146
|
|
|
147
147
|
export interface SelectInputProps<T = string, M extends boolean = false> {
|
|
148
148
|
id?: string;
|
|
149
|
+
/**
|
|
150
|
+
* Sets the `data-wds-parent` attribute on the listbox container, which is needed for complex components like DateInput to correctly manage event handling.
|
|
151
|
+
* @internal
|
|
152
|
+
*/
|
|
153
|
+
parentId?: string;
|
|
149
154
|
name?: string;
|
|
150
155
|
multiple?: M;
|
|
151
156
|
placeholder?: string;
|
|
152
157
|
items: readonly SelectInputItem<NonNullable<T>>[];
|
|
158
|
+
/**
|
|
159
|
+
* Enables browser autocomplete integration through the search input.
|
|
160
|
+
* Accepts standard HTML autocomplete values (e.g., "country-name", "address-level1").
|
|
161
|
+
*
|
|
162
|
+
* Requires `filterable={true}` to enable the search input.
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* <SelectInput
|
|
166
|
+
* name="country"
|
|
167
|
+
* autocomplete="country-name"
|
|
168
|
+
* filterable={true}
|
|
169
|
+
* items={[{
|
|
170
|
+
* type: 'option',
|
|
171
|
+
* value: 'GB',
|
|
172
|
+
* filterMatchers: ['United Kingdom', 'UK']
|
|
173
|
+
* }]}
|
|
174
|
+
* />
|
|
175
|
+
*/
|
|
176
|
+
autocomplete?: string;
|
|
153
177
|
defaultValue?: M extends true ? readonly T[] : T;
|
|
154
178
|
value?: M extends true ? readonly T[] : T;
|
|
155
179
|
compareValues?:
|
|
@@ -176,6 +200,8 @@ export interface SelectInputProps<T = string, M extends boolean = false> {
|
|
|
176
200
|
UNSAFE_triggerButtonProps?: WithInputAttributesProps['inputAttributes'] & {
|
|
177
201
|
'aria-label'?: string;
|
|
178
202
|
};
|
|
203
|
+
/** Ref to the select trigger button element. */
|
|
204
|
+
triggerRef?: React.MutableRefObject<HTMLButtonElement | null>;
|
|
179
205
|
onFilterChange?: (args: { query: string; queryNormalized: string | null }) => void;
|
|
180
206
|
onChange?: (value: M extends true ? T[] : T) => void;
|
|
181
207
|
onOpen?: () => void;
|
|
@@ -246,9 +272,11 @@ const noop = () => {};
|
|
|
246
272
|
|
|
247
273
|
export function SelectInput<T = string, M extends boolean = false>({
|
|
248
274
|
id: idProp,
|
|
275
|
+
parentId,
|
|
249
276
|
name,
|
|
250
277
|
multiple,
|
|
251
278
|
placeholder,
|
|
279
|
+
autocomplete,
|
|
252
280
|
items,
|
|
253
281
|
defaultValue,
|
|
254
282
|
value: controlledValue,
|
|
@@ -262,6 +290,7 @@ export function SelectInput<T = string, M extends boolean = false>({
|
|
|
262
290
|
size = 'md',
|
|
263
291
|
className,
|
|
264
292
|
UNSAFE_triggerButtonProps,
|
|
293
|
+
triggerRef: externalTriggerRef,
|
|
265
294
|
onFilterChange = noop,
|
|
266
295
|
onChange,
|
|
267
296
|
onOpen,
|
|
@@ -300,7 +329,7 @@ export function SelectInput<T = string, M extends boolean = false>({
|
|
|
300
329
|
}
|
|
301
330
|
});
|
|
302
331
|
|
|
303
|
-
const
|
|
332
|
+
const internalTriggerRef = useRef<HTMLButtonElement | null>(null);
|
|
304
333
|
|
|
305
334
|
const screenSm = useScreenSize(Breakpoint.SMALL);
|
|
306
335
|
const OptionsOverlay = screenSm ? Popover : BottomSheet;
|
|
@@ -368,7 +397,12 @@ export function SelectInput<T = string, M extends boolean = false>({
|
|
|
368
397
|
value={{
|
|
369
398
|
ref: (node) => {
|
|
370
399
|
ref(node);
|
|
371
|
-
|
|
400
|
+
if (externalTriggerRef) {
|
|
401
|
+
// eslint-disable-next-line no-param-reassign
|
|
402
|
+
externalTriggerRef.current = node;
|
|
403
|
+
} else {
|
|
404
|
+
internalTriggerRef.current = node;
|
|
405
|
+
}
|
|
372
406
|
},
|
|
373
407
|
...inputAttributes,
|
|
374
408
|
...UNSAFE_triggerButtonProps,
|
|
@@ -411,7 +445,9 @@ export function SelectInput<T = string, M extends boolean = false>({
|
|
|
411
445
|
onClear != null
|
|
412
446
|
? () => {
|
|
413
447
|
onClear();
|
|
414
|
-
|
|
448
|
+
(externalTriggerRef?.current ?? internalTriggerRef.current)?.focus({
|
|
449
|
+
preventScroll: true,
|
|
450
|
+
});
|
|
415
451
|
}
|
|
416
452
|
: undefined,
|
|
417
453
|
disabled: uiDisabled,
|
|
@@ -432,6 +468,7 @@ export function SelectInput<T = string, M extends boolean = false>({
|
|
|
432
468
|
>
|
|
433
469
|
<SelectInputOptions
|
|
434
470
|
id={id ? `${id}Search` : undefined}
|
|
471
|
+
parentId={parentId}
|
|
435
472
|
items={items}
|
|
436
473
|
renderValue={renderValue}
|
|
437
474
|
renderFooter={renderFooter}
|
|
@@ -440,7 +477,15 @@ export function SelectInput<T = string, M extends boolean = false>({
|
|
|
440
477
|
searchInputRef={searchInputRef}
|
|
441
478
|
listboxRef={listboxRef}
|
|
442
479
|
filterQuery={deferredFilterQuery}
|
|
480
|
+
autocomplete={autocomplete}
|
|
481
|
+
name={name}
|
|
443
482
|
onFilterChange={setFilterQuery}
|
|
483
|
+
onAutocompleteSelect={(matchedValue) => {
|
|
484
|
+
onChange?.(matchedValue as M extends true ? T[] : T);
|
|
485
|
+
if (!multiple) {
|
|
486
|
+
setOpen(false);
|
|
487
|
+
}
|
|
488
|
+
}}
|
|
444
489
|
{...getListBoxLabelProps()}
|
|
445
490
|
/>
|
|
446
491
|
</OptionsOverlay>
|
|
@@ -534,7 +579,13 @@ const SelectInputOptionsContainer = forwardRef(function SelectInputOptionsContai
|
|
|
534
579
|
interface SelectInputOptionsProps<T = string>
|
|
535
580
|
extends Pick<
|
|
536
581
|
SelectInputProps<T>,
|
|
537
|
-
|
|
582
|
+
| 'items'
|
|
583
|
+
| 'renderValue'
|
|
584
|
+
| 'renderFooter'
|
|
585
|
+
| 'filterable'
|
|
586
|
+
| 'filterPlaceholder'
|
|
587
|
+
| 'id'
|
|
588
|
+
| 'parentId'
|
|
538
589
|
> {
|
|
539
590
|
searchInputRef: React.MutableRefObject<HTMLInputElement | null>;
|
|
540
591
|
listboxRef: React.MutableRefObject<HTMLDivElement | null>;
|
|
@@ -542,10 +593,14 @@ interface SelectInputOptionsProps<T = string>
|
|
|
542
593
|
onFilterChange: (query: string) => void;
|
|
543
594
|
listBoxLabel?: string;
|
|
544
595
|
listBoxLabelledBy?: string;
|
|
596
|
+
autocomplete?: string;
|
|
597
|
+
name?: string;
|
|
598
|
+
onAutocompleteSelect?: (value: T) => void;
|
|
545
599
|
}
|
|
546
600
|
|
|
547
601
|
function SelectInputOptions<T = string>({
|
|
548
602
|
id,
|
|
603
|
+
parentId,
|
|
549
604
|
items,
|
|
550
605
|
renderValue = String,
|
|
551
606
|
renderFooter,
|
|
@@ -557,6 +612,9 @@ function SelectInputOptions<T = string>({
|
|
|
557
612
|
onFilterChange,
|
|
558
613
|
listBoxLabel,
|
|
559
614
|
listBoxLabelledBy,
|
|
615
|
+
autocomplete,
|
|
616
|
+
name,
|
|
617
|
+
onAutocompleteSelect,
|
|
560
618
|
}: SelectInputOptionsProps<T>) {
|
|
561
619
|
const intl = useIntl();
|
|
562
620
|
const virtualiserHandlerRef = useRef<VirtualizerHandle>(null);
|
|
@@ -640,6 +698,35 @@ function SelectInputOptions<T = string>({
|
|
|
640
698
|
);
|
|
641
699
|
};
|
|
642
700
|
|
|
701
|
+
const findMatchingItem = (autocompleteValue: string): T | null => {
|
|
702
|
+
const flatOptions = items
|
|
703
|
+
.flatMap((item) =>
|
|
704
|
+
item.type === 'group' ? item.options : item.type === 'option' ? [item] : [],
|
|
705
|
+
)
|
|
706
|
+
.filter(
|
|
707
|
+
(item): item is SelectInputOptionItem<NonNullable<T>> =>
|
|
708
|
+
item.type === 'option' && item.value != null,
|
|
709
|
+
);
|
|
710
|
+
|
|
711
|
+
const exactMatch = flatOptions.find(
|
|
712
|
+
(option) =>
|
|
713
|
+
String(option.value) === autocompleteValue ||
|
|
714
|
+
option.filterMatchers?.some((matcher) => matcher === autocompleteValue),
|
|
715
|
+
);
|
|
716
|
+
|
|
717
|
+
if (exactMatch) {
|
|
718
|
+
return exactMatch.value;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
const fuzzyMatch = flatOptions.find((option) =>
|
|
722
|
+
option.filterMatchers?.some((matcher) =>
|
|
723
|
+
matcher.toLowerCase().includes(autocompleteValue.toLowerCase()),
|
|
724
|
+
),
|
|
725
|
+
);
|
|
726
|
+
|
|
727
|
+
return fuzzyMatch ? fuzzyMatch.value : null;
|
|
728
|
+
};
|
|
729
|
+
|
|
643
730
|
return (
|
|
644
731
|
<ListboxBase.Options
|
|
645
732
|
as={SelectInputOptionsContainer}
|
|
@@ -660,6 +747,8 @@ function SelectInputOptions<T = string>({
|
|
|
660
747
|
<SearchInput
|
|
661
748
|
ref={searchInputRef}
|
|
662
749
|
id={id}
|
|
750
|
+
name={name}
|
|
751
|
+
autoComplete={autocomplete}
|
|
663
752
|
role="combobox"
|
|
664
753
|
shape="rectangle"
|
|
665
754
|
placeholder={filterPlaceholder}
|
|
@@ -679,8 +768,26 @@ function SelectInputOptions<T = string>({
|
|
|
679
768
|
onChange={(event) => {
|
|
680
769
|
// Free up resources and ensure not to go out of bounds when the
|
|
681
770
|
// resulting item count is less than before
|
|
771
|
+
const inputValue = event.currentTarget.value;
|
|
772
|
+
|
|
773
|
+
// Free up resources and ensure not to go out of bounds
|
|
682
774
|
setMountedIndexes([]);
|
|
683
|
-
onFilterChange(
|
|
775
|
+
onFilterChange(inputValue);
|
|
776
|
+
}}
|
|
777
|
+
onInput={(event) => {
|
|
778
|
+
const inputValue = event.currentTarget.value;
|
|
779
|
+
const inputElement = event.currentTarget;
|
|
780
|
+
|
|
781
|
+
if (autocomplete && onAutocompleteSelect && inputValue) {
|
|
782
|
+
setTimeout(() => {
|
|
783
|
+
if (inputElement.value === inputValue && inputValue.length > 2) {
|
|
784
|
+
const matchedValue = findMatchingItem(inputValue);
|
|
785
|
+
if (matchedValue !== null) {
|
|
786
|
+
onAutocompleteSelect(matchedValue);
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
}, 50);
|
|
790
|
+
}
|
|
684
791
|
}}
|
|
685
792
|
/>
|
|
686
793
|
</div>
|
|
@@ -696,6 +803,7 @@ function SelectInputOptions<T = string>({
|
|
|
696
803
|
items.some((item) => item.type === 'group') &&
|
|
697
804
|
'np-select-input-listbox-container--has-group',
|
|
698
805
|
)}
|
|
806
|
+
data-wds-parent={parentId ?? undefined}
|
|
699
807
|
>
|
|
700
808
|
{resultsEmpty ? (
|
|
701
809
|
<div id={statusId} className="np-select-input-options-status">
|
package/src/inputs/TextArea.css
CHANGED
|
@@ -18,6 +18,11 @@
|
|
|
18
18
|
transition-duration: 300ms;
|
|
19
19
|
/* TODO: Remove these overrides once `.form-control` isn’t used anymore */
|
|
20
20
|
}
|
|
21
|
+
.disabled .np-form-control,
|
|
22
|
+
:disabled .np-form-control {
|
|
23
|
+
opacity: 1;
|
|
24
|
+
opacity: initial;
|
|
25
|
+
}
|
|
21
26
|
.np-form-control:focus-visible {
|
|
22
27
|
outline: none;
|
|
23
28
|
}
|
package/src/inputs/_common.less
CHANGED
package/src/inputs/contexts.tsx
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { createContext, useContext } from 'react';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
type FieldLabelContextType = {
|
|
4
|
+
id?: string;
|
|
5
|
+
ref?: React.RefObject<HTMLLabelElement>;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
const FieldLabelContext = createContext<FieldLabelContextType | undefined>(undefined);
|
|
9
|
+
export const FieldLabelContextProvider = FieldLabelContext.Provider;
|
|
5
10
|
|
|
6
11
|
const InputIdContext = createContext<string | undefined>(undefined);
|
|
7
12
|
export const InputIdContextProvider = InputIdContext.Provider;
|
|
@@ -18,7 +23,7 @@ interface UseInputAttributesArgs {
|
|
|
18
23
|
}
|
|
19
24
|
|
|
20
25
|
export function useInputAttributes({ nonLabelable }: UseInputAttributesArgs = {}) {
|
|
21
|
-
const labelId = useContext(
|
|
26
|
+
const labelId = useContext(FieldLabelContext)?.id;
|
|
22
27
|
return {
|
|
23
28
|
id: useContext(InputIdContext),
|
|
24
29
|
'aria-labelledby': nonLabelable ? labelId : undefined,
|
|
@@ -27,6 +32,10 @@ export function useInputAttributes({ nonLabelable }: UseInputAttributesArgs = {}
|
|
|
27
32
|
} satisfies React.HTMLAttributes<HTMLElement>;
|
|
28
33
|
}
|
|
29
34
|
|
|
35
|
+
export function useFieldLabelRef() {
|
|
36
|
+
return useContext(FieldLabelContext)?.ref;
|
|
37
|
+
}
|
|
38
|
+
|
|
30
39
|
export interface WithInputAttributesProps {
|
|
31
40
|
inputAttributes: ReturnType<typeof useInputAttributes>;
|
|
32
41
|
}
|
package/src/label/Label.tsx
CHANGED
|
@@ -3,7 +3,7 @@ import messages from './Label.messages';
|
|
|
3
3
|
import { useIntl } from 'react-intl';
|
|
4
4
|
import Body from '../body';
|
|
5
5
|
import { CommonProps } from '../common';
|
|
6
|
-
import { PropsWithChildren } from 'react';
|
|
6
|
+
import { forwardRef, PropsWithChildren } from 'react';
|
|
7
7
|
|
|
8
8
|
export type LabelProps = {
|
|
9
9
|
id?: string;
|
|
@@ -21,25 +21,29 @@ export type LabelProps = {
|
|
|
21
21
|
* <Field label={..} description={..} required={..}>..</Field>
|
|
22
22
|
* ```
|
|
23
23
|
*/
|
|
24
|
-
const Label =
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
className
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
24
|
+
const Label = forwardRef<HTMLLabelElement, LabelProps>(
|
|
25
|
+
({ className, children, htmlFor, id }: LabelProps, ref) => {
|
|
26
|
+
return (
|
|
27
|
+
<label
|
|
28
|
+
ref={ref}
|
|
29
|
+
id={id}
|
|
30
|
+
htmlFor={htmlFor}
|
|
31
|
+
className={clsx(
|
|
32
|
+
'np-label d-flex flex-column np-text-body-default-bold text-primary m-b-0',
|
|
33
|
+
className,
|
|
34
|
+
)}
|
|
35
|
+
>
|
|
36
|
+
{children}
|
|
37
|
+
</label>
|
|
38
|
+
);
|
|
39
|
+
},
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
Label.displayName = 'Label';
|
|
38
43
|
|
|
39
44
|
export type LabelOptionalProps = PropsWithChildren<CommonProps>;
|
|
40
45
|
|
|
41
|
-
|
|
42
|
-
Label.Optional = function Optional({ children, className }: LabelOptionalProps) {
|
|
46
|
+
const Optional = function Optional({ children, className }: LabelOptionalProps) {
|
|
43
47
|
const { formatMessage } = useIntl();
|
|
44
48
|
return (
|
|
45
49
|
<div>
|
|
@@ -53,8 +57,7 @@ Label.Optional = function Optional({ children, className }: LabelOptionalProps)
|
|
|
53
57
|
|
|
54
58
|
export type LabelDescriptionProps = PropsWithChildren<CommonProps> & { id?: string };
|
|
55
59
|
|
|
56
|
-
|
|
57
|
-
Label.Description = function Description({ id, children, className }: LabelDescriptionProps) {
|
|
60
|
+
const Description = function Description({ id, children, className }: LabelDescriptionProps) {
|
|
58
61
|
return children ? (
|
|
59
62
|
<Body id={id} className={clsx('text-secondary', className)}>
|
|
60
63
|
{children}
|
|
@@ -62,4 +65,7 @@ Label.Description = function Description({ id, children, className }: LabelDescr
|
|
|
62
65
|
) : null;
|
|
63
66
|
};
|
|
64
67
|
|
|
65
|
-
|
|
68
|
+
// eslint-disable-next-line functional/immutable-data
|
|
69
|
+
const LabelNamespace = Object.assign(Label, { Optional, Description });
|
|
70
|
+
|
|
71
|
+
export { LabelNamespace as Label };
|
|
@@ -47,6 +47,8 @@ const BADGES = {
|
|
|
47
47
|
</div>
|
|
48
48
|
),
|
|
49
49
|
},
|
|
50
|
+
'Notification badge': { type: 'notification' },
|
|
51
|
+
'Online badge': { type: 'online' },
|
|
50
52
|
} as const;
|
|
51
53
|
|
|
52
54
|
export default {
|
|
@@ -61,7 +63,6 @@ export default {
|
|
|
61
63
|
size: 48,
|
|
62
64
|
selected: false,
|
|
63
65
|
badge: { type: 'action' },
|
|
64
|
-
notification: false,
|
|
65
66
|
profileName: undefined,
|
|
66
67
|
profileType: undefined,
|
|
67
68
|
imgSrc: undefined,
|
|
@@ -88,20 +89,23 @@ export default {
|
|
|
88
89
|
type: { summary: 'ProfileType' },
|
|
89
90
|
},
|
|
90
91
|
},
|
|
91
|
-
notification: {
|
|
92
|
-
control: 'boolean',
|
|
93
|
-
description: 'Shows notification dot',
|
|
94
|
-
},
|
|
95
|
-
selected: {
|
|
96
|
-
control: 'boolean',
|
|
97
|
-
description: 'Toggles selected state',
|
|
98
|
-
},
|
|
99
92
|
badge: {
|
|
100
|
-
|
|
93
|
+
control: 'select',
|
|
94
|
+
options: ['action', 'notification', 'online'],
|
|
95
|
+
description: 'Badge type',
|
|
96
|
+
mapping: {
|
|
97
|
+
action: { type: 'action' },
|
|
98
|
+
notification: { type: 'notification' },
|
|
99
|
+
online: { type: 'online' },
|
|
100
|
+
},
|
|
101
101
|
table: {
|
|
102
102
|
type: { summary: 'AvatarViewBadgeProps' },
|
|
103
103
|
},
|
|
104
104
|
},
|
|
105
|
+
selected: {
|
|
106
|
+
control: 'boolean',
|
|
107
|
+
description: 'Toggles selected state',
|
|
108
|
+
},
|
|
105
109
|
children: {
|
|
106
110
|
table: {
|
|
107
111
|
type: { summary: 'ReactNode' },
|
|
@@ -243,8 +247,48 @@ export const Sizes: Story = {
|
|
|
243
247
|
};
|
|
244
248
|
|
|
245
249
|
/**
|
|
246
|
-
*
|
|
247
|
-
*
|
|
250
|
+
* AvatarView supports different types of badges for additional context and information. <br />
|
|
251
|
+
* Refer to the [design documentation](https://wise.design/components/avatar#:~:text=support%20the%20information.-,With%20badge,-Badges%20contain%20additional) for details.
|
|
252
|
+
*/
|
|
253
|
+
export const Badges: Story = {
|
|
254
|
+
args: {
|
|
255
|
+
imgSrc: '../avatar-square-dude.webp',
|
|
256
|
+
},
|
|
257
|
+
argTypes: hideControls([
|
|
258
|
+
'profileName',
|
|
259
|
+
'imgSrc',
|
|
260
|
+
'profileType',
|
|
261
|
+
'notification',
|
|
262
|
+
'dot',
|
|
263
|
+
'selected',
|
|
264
|
+
]),
|
|
265
|
+
parameters: {
|
|
266
|
+
docs: {
|
|
267
|
+
canvas: {
|
|
268
|
+
sourceState: 'hidden',
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
},
|
|
272
|
+
render: (args) => {
|
|
273
|
+
return (
|
|
274
|
+
<List>
|
|
275
|
+
{Object.entries(BADGES).map(([title, badge]) => (
|
|
276
|
+
<ListItem
|
|
277
|
+
key={title}
|
|
278
|
+
title={title}
|
|
279
|
+
subtitle={lorem10}
|
|
280
|
+
media={<ListItem.AvatarView {...args} badge={badge} />}
|
|
281
|
+
control={CONTROLS.iconButton}
|
|
282
|
+
/>
|
|
283
|
+
))}
|
|
284
|
+
</List>
|
|
285
|
+
);
|
|
286
|
+
},
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* `notification` is a particular `type` of Badge that appears in the top right corner as a red dot. It also adjusts to the Avatar's size. <br />
|
|
291
|
+
* **NB:** You can only choose one badge at a time, so the notification badge cannot be combined with any other badge type.
|
|
248
292
|
*/
|
|
249
293
|
export const Notification: Story = {
|
|
250
294
|
parameters: {
|
|
@@ -263,6 +307,7 @@ export const Notification: Story = {
|
|
|
263
307
|
'profileType',
|
|
264
308
|
'size',
|
|
265
309
|
'notification',
|
|
310
|
+
'dot',
|
|
266
311
|
'selected',
|
|
267
312
|
]),
|
|
268
313
|
render: (args) => {
|
|
@@ -274,7 +319,7 @@ export const Notification: Story = {
|
|
|
274
319
|
title={`Size ${size}`}
|
|
275
320
|
subtitle={lorem10}
|
|
276
321
|
media={
|
|
277
|
-
<ListItem.AvatarView {...args} size={size} notification>
|
|
322
|
+
<ListItem.AvatarView {...args} size={size} badge={{ type: 'notification' }}>
|
|
278
323
|
<Taxi />
|
|
279
324
|
</ListItem.AvatarView>
|
|
280
325
|
}
|
|
@@ -287,14 +332,11 @@ export const Notification: Story = {
|
|
|
287
332
|
};
|
|
288
333
|
|
|
289
334
|
/**
|
|
290
|
-
*
|
|
291
|
-
*
|
|
335
|
+
* Similarly, Badge also has an `online` `type` which is green and also adjusts to the Avatar's size. <br />
|
|
336
|
+
* **NB:** You can only choose one badge at a time, so the online badge cannot be combined with any other badge type.
|
|
292
337
|
*/
|
|
293
|
-
export const
|
|
294
|
-
|
|
295
|
-
imgSrc: '../avatar-square-dude.webp',
|
|
296
|
-
},
|
|
297
|
-
argTypes: hideControls(['profileName', 'imgSrc', 'profileType', 'notification', 'selected']),
|
|
338
|
+
export const Online: Story = {
|
|
339
|
+
tags: ['new'],
|
|
298
340
|
parameters: {
|
|
299
341
|
docs: {
|
|
300
342
|
canvas: {
|
|
@@ -302,15 +344,30 @@ export const Badges: Story = {
|
|
|
302
344
|
},
|
|
303
345
|
},
|
|
304
346
|
},
|
|
347
|
+
args: {
|
|
348
|
+
badge: undefined,
|
|
349
|
+
},
|
|
350
|
+
argTypes: hideControls([
|
|
351
|
+
'profileName',
|
|
352
|
+
'imgSrc',
|
|
353
|
+
'profileType',
|
|
354
|
+
'size',
|
|
355
|
+
'notification',
|
|
356
|
+
'selected',
|
|
357
|
+
]),
|
|
305
358
|
render: (args) => {
|
|
306
359
|
return (
|
|
307
360
|
<List>
|
|
308
|
-
{
|
|
361
|
+
{SIZES.map((size) => (
|
|
309
362
|
<ListItem
|
|
310
|
-
key={
|
|
311
|
-
title={
|
|
363
|
+
key={size}
|
|
364
|
+
title={`Size ${size}`}
|
|
312
365
|
subtitle={lorem10}
|
|
313
|
-
media={
|
|
366
|
+
media={
|
|
367
|
+
<ListItem.AvatarView {...args} size={size} badge={{ type: 'online' }}>
|
|
368
|
+
<Taxi />
|
|
369
|
+
</ListItem.AvatarView>
|
|
370
|
+
}
|
|
314
371
|
control={CONTROLS.iconButton}
|
|
315
372
|
/>
|
|
316
373
|
))}
|
|
@@ -323,7 +380,14 @@ export const Badges: Story = {
|
|
|
323
380
|
* AvatarView supports selected state for interactive contexts.
|
|
324
381
|
*/
|
|
325
382
|
export const Selected: Story = {
|
|
326
|
-
argTypes: hideControls([
|
|
383
|
+
argTypes: hideControls([
|
|
384
|
+
'profileName',
|
|
385
|
+
'imgSrc',
|
|
386
|
+
'profileType',
|
|
387
|
+
'notification',
|
|
388
|
+
'dot',
|
|
389
|
+
'selected',
|
|
390
|
+
]),
|
|
327
391
|
render: (args) => {
|
|
328
392
|
return (
|
|
329
393
|
<List>
|