@transferwise/components 0.0.0-experimental-17cb250 → 0.0.0-experimental-333df2c
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/dateLookup/dateTrigger/DateTrigger.js +4 -8
- package/build/dateLookup/dateTrigger/DateTrigger.js.map +1 -1
- package/build/dateLookup/dateTrigger/DateTrigger.mjs +4 -8
- package/build/dateLookup/dateTrigger/DateTrigger.mjs.map +1 -1
- package/build/field/Field.js +2 -9
- package/build/field/Field.js.map +1 -1
- package/build/field/Field.mjs +2 -9
- package/build/field/Field.mjs.map +1 -1
- package/build/i18n/en.json +1 -3
- package/build/i18n/en.json.js +1 -3
- package/build/i18n/en.json.js.map +1 -1
- package/build/i18n/en.json.mjs +1 -3
- package/build/i18n/en.json.mjs.map +1 -1
- package/build/inputs/SelectInput.js +86 -34
- package/build/inputs/SelectInput.js.map +1 -1
- package/build/inputs/SelectInput.mjs +88 -36
- package/build/inputs/SelectInput.mjs.map +1 -1
- package/build/label/Label.js +1 -29
- package/build/label/Label.js.map +1 -1
- package/build/label/Label.mjs +2 -30
- package/build/label/Label.mjs.map +1 -1
- package/build/main.css +18 -0
- package/build/styles/dateLookup/dateTrigger/DateTrigger.css +8 -0
- package/build/styles/inputs/SelectInput.css +10 -0
- package/build/styles/main.css +18 -0
- package/build/types/dateLookup/dateTrigger/DateTrigger.d.ts.map +1 -1
- package/build/types/field/Field.d.ts +2 -4
- package/build/types/field/Field.d.ts.map +1 -1
- package/build/types/index.d.ts +1 -1
- package/build/types/index.d.ts.map +1 -1
- package/build/types/inputs/SelectInput.d.ts.map +1 -1
- package/build/types/label/Label.d.ts +1 -10
- package/build/types/label/Label.d.ts.map +1 -1
- package/package.json +5 -4
- package/src/dateInput/DateInput.tests.story.tsx +32 -8
- package/src/dateLookup/DateLookup.rtl.spec.tsx +1 -1
- package/src/dateLookup/dateTrigger/DateTrigger.css +8 -0
- package/src/dateLookup/dateTrigger/DateTrigger.less +8 -0
- package/src/dateLookup/dateTrigger/DateTrigger.spec.js +1 -1
- package/src/dateLookup/dateTrigger/DateTrigger.tsx +4 -9
- package/src/field/Field.spec.tsx +3 -3
- package/src/field/Field.story.tsx +3 -81
- package/src/field/Field.tests.story.tsx +33 -0
- package/src/field/Field.tsx +4 -10
- package/src/i18n/en.json +1 -3
- package/src/index.ts +1 -1
- package/src/inlineAlert/InlineAlert.story.tsx +21 -8
- package/src/inputs/InputGroup.spec.tsx +1 -1
- package/src/inputs/SearchInput.spec.tsx +1 -1
- package/src/inputs/SelectInput.css +10 -0
- package/src/inputs/SelectInput.less +12 -0
- package/src/inputs/SelectInput.spec.tsx +1 -1
- package/src/inputs/SelectInput.story.tsx +20 -0
- package/src/inputs/SelectInput.tsx +116 -37
- package/src/label/Label.story.tsx +21 -37
- package/src/label/Label.tsx +2 -42
- package/src/main.css +18 -0
- package/src/radioGroup/RadioGroup.rtl.spec.tsx +1 -1
- package/src/select/Select.rtl.spec.tsx +1 -1
- package/src/switch/Switch.spec.tsx +1 -1
- package/build/label/Label.messages.js +0 -15
- package/build/label/Label.messages.js.map +0 -1
- package/build/label/Label.messages.mjs +0 -13
- package/build/label/Label.messages.mjs.map +0 -1
- package/build/types/label/Label.messages.d.ts +0 -12
- package/build/types/label/Label.messages.d.ts.map +0 -1
- package/build/types/label/index.d.ts +0 -3
- package/build/types/label/index.d.ts.map +0 -1
- package/src/label/Label.messages.tsx +0 -12
- package/src/label/index.ts +0 -2
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
createContext,
|
|
7
7
|
forwardRef,
|
|
8
8
|
useContext,
|
|
9
|
+
useDeferredValue,
|
|
9
10
|
useEffect,
|
|
10
11
|
useId,
|
|
11
12
|
useMemo,
|
|
@@ -13,6 +14,7 @@ import {
|
|
|
13
14
|
useState,
|
|
14
15
|
} from 'react';
|
|
15
16
|
import { useIntl } from 'react-intl';
|
|
17
|
+
import { Virtualizer } from 'virtua';
|
|
16
18
|
|
|
17
19
|
import { useEffectEvent } from '../common/hooks/useEffectEvent';
|
|
18
20
|
import { useScreenSize } from '../common/hooks/useScreenSize';
|
|
@@ -29,6 +31,8 @@ import { InputGroup } from './InputGroup';
|
|
|
29
31
|
import { SearchInput } from './SearchInput';
|
|
30
32
|
import messages from './SelectInput.messages';
|
|
31
33
|
|
|
34
|
+
const MAX_ITEMS_WITHOUT_VIRTUALIZATION = 50;
|
|
35
|
+
|
|
32
36
|
function searchableString(value: string) {
|
|
33
37
|
return value.trim().replace(/\s+/gu, ' ').normalize('NFKC').toLowerCase();
|
|
34
38
|
}
|
|
@@ -40,7 +44,7 @@ function inferSearchableStrings(value: unknown) {
|
|
|
40
44
|
|
|
41
45
|
if (typeof value === 'object' && value != null) {
|
|
42
46
|
return Object.values(value)
|
|
43
|
-
.filter((innerValue)
|
|
47
|
+
.filter((innerValue) => typeof innerValue === 'string')
|
|
44
48
|
.map((innerValue) => searchableString(innerValue));
|
|
45
49
|
}
|
|
46
50
|
|
|
@@ -89,6 +93,11 @@ function dedupeSelectInputOptionItem<T>(
|
|
|
89
93
|
return { ...item, value: undefined };
|
|
90
94
|
}
|
|
91
95
|
|
|
96
|
+
/**
|
|
97
|
+
* Sets the `value` of duplicate option items to `undefined`, hiding them when
|
|
98
|
+
* rendered. Indexes are kept intact within groups to preserve the active item
|
|
99
|
+
* between filter changes when possible.
|
|
100
|
+
*/
|
|
92
101
|
function dedupeSelectInputItems<T>(
|
|
93
102
|
items: readonly SelectInputItem<T>[],
|
|
94
103
|
): SelectInputItem<T | undefined>[] {
|
|
@@ -112,20 +121,23 @@ function dedupeSelectInputItems<T>(
|
|
|
112
121
|
});
|
|
113
122
|
}
|
|
114
123
|
|
|
115
|
-
function
|
|
124
|
+
function selectInputOptionItemIncludesNeedle<T>(item: SelectInputOptionItem<T>, needle: string) {
|
|
116
125
|
return inferSearchableStrings(item.filterMatchers ?? item.value).some((haystack) =>
|
|
117
126
|
haystack.includes(needle),
|
|
118
127
|
);
|
|
119
128
|
}
|
|
120
129
|
|
|
121
|
-
function filterSelectInputItems<T>(
|
|
130
|
+
function filterSelectInputItems<T>(
|
|
131
|
+
items: readonly SelectInputItem<T>[],
|
|
132
|
+
predicate: (item: SelectInputOptionItem<T>) => boolean,
|
|
133
|
+
) {
|
|
122
134
|
return items.filter((item) => {
|
|
123
135
|
switch (item.type) {
|
|
124
136
|
case 'option': {
|
|
125
|
-
return
|
|
137
|
+
return predicate(item);
|
|
126
138
|
}
|
|
127
139
|
case 'group': {
|
|
128
|
-
return item.options.some((option) =>
|
|
140
|
+
return item.options.some((option) => predicate(option));
|
|
129
141
|
}
|
|
130
142
|
default:
|
|
131
143
|
}
|
|
@@ -271,12 +283,15 @@ export function SelectInput<T = string, M extends boolean = false>({
|
|
|
271
283
|
}, [handleClose, open]);
|
|
272
284
|
|
|
273
285
|
const [filterQuery, _setFilterQuery] = useState('');
|
|
286
|
+
const deferredFilterQuery = useDeferredValue(filterQuery);
|
|
274
287
|
const setFilterQuery = useEffectEvent((query: string) => {
|
|
275
288
|
_setFilterQuery(query);
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
289
|
+
if (query !== filterQuery) {
|
|
290
|
+
onFilterChange({
|
|
291
|
+
query,
|
|
292
|
+
queryNormalized: query ? searchableString(query) : null,
|
|
293
|
+
});
|
|
294
|
+
}
|
|
280
295
|
});
|
|
281
296
|
|
|
282
297
|
const triggerRef = useRef<HTMLButtonElement | null>(null);
|
|
@@ -294,9 +309,7 @@ export function SelectInput<T = string, M extends boolean = false>({
|
|
|
294
309
|
multiple={multiple}
|
|
295
310
|
defaultValue={defaultValue}
|
|
296
311
|
value={controlledValue}
|
|
297
|
-
|
|
298
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
299
|
-
by={compareValues as any}
|
|
312
|
+
by={compareValues}
|
|
300
313
|
disabled={disabled}
|
|
301
314
|
onChange={
|
|
302
315
|
((value) => {
|
|
@@ -349,8 +362,8 @@ export function SelectInput<T = string, M extends boolean = false>({
|
|
|
349
362
|
content: !placeholderShown ? (
|
|
350
363
|
<SelectInputOptionContentWithinTriggerContext.Provider value>
|
|
351
364
|
{multiple && Array.isArray(value)
|
|
352
|
-
? value
|
|
353
|
-
.map((option
|
|
365
|
+
? (value as readonly NonNullable<T>[])
|
|
366
|
+
.map((option) => renderValue(option, true))
|
|
354
367
|
.filter((node) => node != null)
|
|
355
368
|
.join(', ')
|
|
356
369
|
: renderValue(value as NonNullable<T>, true)}
|
|
@@ -379,9 +392,7 @@ export function SelectInput<T = string, M extends boolean = false>({
|
|
|
379
392
|
setOpen(false);
|
|
380
393
|
}}
|
|
381
394
|
onCloseEnd={() => {
|
|
382
|
-
|
|
383
|
-
setFilterQuery('');
|
|
384
|
-
}
|
|
395
|
+
setFilterQuery('');
|
|
385
396
|
}}
|
|
386
397
|
>
|
|
387
398
|
<SelectInputOptions
|
|
@@ -392,7 +403,7 @@ export function SelectInput<T = string, M extends boolean = false>({
|
|
|
392
403
|
filterPlaceholder={filterPlaceholder}
|
|
393
404
|
searchInputRef={searchInputRef}
|
|
394
405
|
listboxRef={listboxRef}
|
|
395
|
-
filterQuery={
|
|
406
|
+
filterQuery={deferredFilterQuery}
|
|
396
407
|
onFilterChange={setFilterQuery}
|
|
397
408
|
/>
|
|
398
409
|
</OptionsOverlay>
|
|
@@ -506,7 +517,48 @@ function SelectInputOptions<T = string>({
|
|
|
506
517
|
}
|
|
507
518
|
return undefined;
|
|
508
519
|
}, [filterQuery, filterable]);
|
|
509
|
-
|
|
520
|
+
useEffect(() => {
|
|
521
|
+
if (needle) {
|
|
522
|
+
// Ensure having an active option while filtering
|
|
523
|
+
requestAnimationFrame(() => {
|
|
524
|
+
if (
|
|
525
|
+
controllerRef.current != null &&
|
|
526
|
+
!controllerRef.current.hasAttribute('aria-activedescendant')
|
|
527
|
+
) {
|
|
528
|
+
// Activate first option via synthetic key press
|
|
529
|
+
controllerRef.current.dispatchEvent(
|
|
530
|
+
new KeyboardEvent('keydown', { key: 'Home', bubbles: true }),
|
|
531
|
+
);
|
|
532
|
+
}
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
}, [controllerRef, needle]);
|
|
536
|
+
|
|
537
|
+
const filteredItems: readonly SelectInputItem<NonNullable<T> | undefined>[] =
|
|
538
|
+
needle != null
|
|
539
|
+
? filterSelectInputItems(dedupeSelectInputItems(items), (item) =>
|
|
540
|
+
selectInputOptionItemIncludesNeedle(item, needle),
|
|
541
|
+
)
|
|
542
|
+
: items;
|
|
543
|
+
const resultsEmpty = needle != null && filteredItems.length === 0;
|
|
544
|
+
|
|
545
|
+
const virtualized = filteredItems.length > MAX_ITEMS_WITHOUT_VIRTUALIZATION;
|
|
546
|
+
|
|
547
|
+
// Items shown once shall be kept mounted until the needle changes, otherwise
|
|
548
|
+
// the scroll position may jump around inadvertently. Pattern adopted from:
|
|
549
|
+
// https://inokawa.github.io/virtua/?path=/story/advanced-keep-offscreen-items--append-only
|
|
550
|
+
const [mountedIndexes, setMountedIndexes] = useState<number[]>([]);
|
|
551
|
+
useEffect(() => {
|
|
552
|
+
// Ensure the 'End' key works as intended by keeping the last item mounted
|
|
553
|
+
setMountedIndexes((prevMountedIndexes) => {
|
|
554
|
+
const indexes = new Set(prevMountedIndexes);
|
|
555
|
+
indexes.add(filteredItems.length - 1);
|
|
556
|
+
return [...indexes]; // Sorting is redundant by nature here
|
|
557
|
+
});
|
|
558
|
+
}, [
|
|
559
|
+
needle, // Needed as `filteredItems.length` may be equal between two updates
|
|
560
|
+
filteredItems.length,
|
|
561
|
+
]);
|
|
510
562
|
|
|
511
563
|
const listboxContainerRef = useRef<HTMLDivElement>(null);
|
|
512
564
|
useEffect(() => {
|
|
@@ -522,6 +574,19 @@ function SelectInputOptions<T = string>({
|
|
|
522
574
|
const statusId = useId();
|
|
523
575
|
const listboxId = useId();
|
|
524
576
|
|
|
577
|
+
const getItemNode = (index: number) => {
|
|
578
|
+
const item = filteredItems[index];
|
|
579
|
+
return (
|
|
580
|
+
<SelectInputItemView
|
|
581
|
+
// eslint-disable-next-line react/no-array-index-key
|
|
582
|
+
key={index}
|
|
583
|
+
item={item}
|
|
584
|
+
renderValue={renderValue}
|
|
585
|
+
needle={needle}
|
|
586
|
+
/>
|
|
587
|
+
);
|
|
588
|
+
};
|
|
589
|
+
|
|
525
590
|
return (
|
|
526
591
|
<ListboxBase.Options
|
|
527
592
|
as={SelectInputOptionsContainer}
|
|
@@ -533,12 +598,6 @@ function SelectInputOptions<T = string>({
|
|
|
533
598
|
controllerRef.current.setAttribute('aria-activedescendant', value);
|
|
534
599
|
} else {
|
|
535
600
|
controllerRef.current.removeAttribute('aria-activedescendant');
|
|
536
|
-
if (filterQuery) {
|
|
537
|
-
// Ensure having an active option while filtering
|
|
538
|
-
controllerRef.current.dispatchEvent(
|
|
539
|
-
new KeyboardEvent('keydown', { key: 'Home', bubbles: true }),
|
|
540
|
-
);
|
|
541
|
-
}
|
|
542
601
|
}
|
|
543
602
|
}
|
|
544
603
|
}}
|
|
@@ -549,7 +608,7 @@ function SelectInputOptions<T = string>({
|
|
|
549
608
|
ref={searchInputRef}
|
|
550
609
|
shape="rectangle"
|
|
551
610
|
placeholder={filterPlaceholder}
|
|
552
|
-
|
|
611
|
+
defaultValue={filterQuery}
|
|
553
612
|
aria-controls={listboxId}
|
|
554
613
|
aria-describedby={showStatus ? statusId : undefined}
|
|
555
614
|
onKeyDown={(event) => {
|
|
@@ -560,6 +619,9 @@ function SelectInputOptions<T = string>({
|
|
|
560
619
|
}
|
|
561
620
|
}}
|
|
562
621
|
onChange={(event) => {
|
|
622
|
+
// Free up resources and ensure not to go out of bounds when the
|
|
623
|
+
// resulting item count is less than before
|
|
624
|
+
setMountedIndexes([]);
|
|
563
625
|
onFilterChange(event.currentTarget.value);
|
|
564
626
|
}}
|
|
565
627
|
/>
|
|
@@ -571,7 +633,9 @@ function SelectInputOptions<T = string>({
|
|
|
571
633
|
tabIndex={-1}
|
|
572
634
|
className={clsx(
|
|
573
635
|
'np-select-input-listbox-container',
|
|
574
|
-
|
|
636
|
+
virtualized && 'np-select-input-listbox-container--virtualized',
|
|
637
|
+
needle == null && // Groups aren't shown when filtering
|
|
638
|
+
items.some((item) => item.type === 'group') &&
|
|
575
639
|
'np-select-input-listbox-container--has-group',
|
|
576
640
|
)}
|
|
577
641
|
>
|
|
@@ -590,15 +654,27 @@ function SelectInputOptions<T = string>({
|
|
|
590
654
|
tabIndex={0}
|
|
591
655
|
className="np-select-input-listbox"
|
|
592
656
|
>
|
|
593
|
-
{
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
657
|
+
{!virtualized ? (
|
|
658
|
+
filteredItems.map((_, index) => getItemNode(index))
|
|
659
|
+
) : (
|
|
660
|
+
<Virtualizer
|
|
661
|
+
key={needle}
|
|
662
|
+
count={filteredItems.length}
|
|
663
|
+
keepMounted={mountedIndexes}
|
|
664
|
+
scrollRef={listboxRef} // `VList` doesn't expose this
|
|
665
|
+
onRangeChange={(startIndex, endIndex) => {
|
|
666
|
+
setMountedIndexes((prevMountedIndexes) => {
|
|
667
|
+
const indexes = new Set(prevMountedIndexes);
|
|
668
|
+
for (let index = startIndex; index <= endIndex; index += 1) {
|
|
669
|
+
indexes.add(index);
|
|
670
|
+
}
|
|
671
|
+
return [...indexes].sort((a, b) => a - b);
|
|
672
|
+
});
|
|
673
|
+
}}
|
|
674
|
+
>
|
|
675
|
+
{(index) => getItemNode(index)}
|
|
676
|
+
</Virtualizer>
|
|
677
|
+
)}
|
|
602
678
|
</div>
|
|
603
679
|
|
|
604
680
|
{renderFooter != null ? (
|
|
@@ -639,7 +715,10 @@ function SelectInputItemView<T = string>({
|
|
|
639
715
|
}: SelectInputItemViewProps<T>) {
|
|
640
716
|
switch (item.type) {
|
|
641
717
|
case 'option': {
|
|
642
|
-
if (
|
|
718
|
+
if (
|
|
719
|
+
item.value != null &&
|
|
720
|
+
(needle == null || selectInputOptionItemIncludesNeedle(item, needle))
|
|
721
|
+
) {
|
|
643
722
|
return (
|
|
644
723
|
<SelectInputOption value={item.value} disabled={item.disabled}>
|
|
645
724
|
{renderValue(item.value, false)}
|
|
@@ -1,53 +1,37 @@
|
|
|
1
1
|
import { useState } from 'react';
|
|
2
2
|
|
|
3
|
+
import Info from '../info/Info';
|
|
3
4
|
import { Input } from '../inputs/Input';
|
|
4
5
|
import { Label } from './Label';
|
|
5
|
-
import InlineAlert from '../inlineAlert/InlineAlert';
|
|
6
|
-
import { lorem10 } from '../test-utils';
|
|
7
6
|
|
|
8
7
|
export default {
|
|
9
8
|
component: Label,
|
|
10
9
|
title: 'Label',
|
|
11
|
-
tags: ['autodocs'],
|
|
12
10
|
};
|
|
13
11
|
|
|
14
12
|
export const Basic = () => {
|
|
15
13
|
const [value, setValue] = useState<string | undefined>('This is some text');
|
|
16
14
|
return (
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
<Label className="m-b-2">
|
|
24
|
-
<Label.Optional>Phone number</Label.Optional>
|
|
25
|
-
<Input value={value} id="input" onChange={({ target }) => setValue(target.value)} />
|
|
26
|
-
</Label>
|
|
27
|
-
|
|
28
|
-
<Label className="m-b-2">
|
|
29
|
-
<Label.Optional>Phone number</Label.Optional>
|
|
30
|
-
<Label.Description>This an field Description</Label.Description>
|
|
31
|
-
<Input value={value} id="input" onChange={({ target }) => setValue(target.value)} />
|
|
32
|
-
</Label>
|
|
33
|
-
|
|
34
|
-
<Label htmlFor="phone-number-1">
|
|
35
|
-
<Label.Optional>Phone number</Label.Optional>
|
|
36
|
-
<Label.Description>This an field Description</Label.Description>
|
|
37
|
-
</Label>
|
|
38
|
-
<Input
|
|
39
|
-
id="phone-number-1"
|
|
40
|
-
className="m-b-2"
|
|
41
|
-
value={value}
|
|
42
|
-
onChange={({ target }) => setValue(target.value)}
|
|
43
|
-
/>
|
|
15
|
+
<Label>
|
|
16
|
+
Phone number
|
|
17
|
+
<Input value={value} id="input" onChange={({ target }) => setValue(target.value)} />
|
|
18
|
+
</Label>
|
|
19
|
+
);
|
|
20
|
+
};
|
|
44
21
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
<
|
|
50
|
-
|
|
51
|
-
|
|
22
|
+
export const WithInfo = () => {
|
|
23
|
+
const [value, setValue] = useState<string | undefined>('This is some text');
|
|
24
|
+
return (
|
|
25
|
+
<Label>
|
|
26
|
+
<span className="d-flex">
|
|
27
|
+
Phone number{' '}
|
|
28
|
+
<Info
|
|
29
|
+
content="This is some help in popover"
|
|
30
|
+
aria-label="The aria label"
|
|
31
|
+
className="m-l-1"
|
|
32
|
+
/>
|
|
33
|
+
</span>
|
|
34
|
+
<Input value={value} id="input" onChange={({ target }) => setValue(target.value)} />
|
|
35
|
+
</Label>
|
|
52
36
|
);
|
|
53
37
|
};
|
package/src/label/Label.tsx
CHANGED
|
@@ -1,9 +1,4 @@
|
|
|
1
1
|
import { clsx } from 'clsx';
|
|
2
|
-
import messages from './Label.messages';
|
|
3
|
-
import { useIntl } from 'react-intl';
|
|
4
|
-
import Body from '../body';
|
|
5
|
-
import { CommonProps } from '../common';
|
|
6
|
-
import { PropsWithChildren } from 'react';
|
|
7
2
|
|
|
8
3
|
export type LabelProps = {
|
|
9
4
|
id?: string;
|
|
@@ -12,49 +7,14 @@ export type LabelProps = {
|
|
|
12
7
|
children?: React.ReactNode;
|
|
13
8
|
};
|
|
14
9
|
|
|
15
|
-
const Label = ({ id, htmlFor, className, children }: LabelProps) => {
|
|
10
|
+
export const Label = ({ id, htmlFor, className, children }: LabelProps) => {
|
|
16
11
|
return (
|
|
17
12
|
<label
|
|
18
13
|
id={id}
|
|
19
14
|
htmlFor={htmlFor}
|
|
20
|
-
className={clsx(
|
|
21
|
-
'd-flex',
|
|
22
|
-
'flex-column',
|
|
23
|
-
'm-b-0',
|
|
24
|
-
'np-text-body-default-bold',
|
|
25
|
-
'text-primary',
|
|
26
|
-
className,
|
|
27
|
-
)}
|
|
15
|
+
className={clsx('control-label d-flex flex-column gap-y-1 m-b-0', className)}
|
|
28
16
|
>
|
|
29
17
|
{children}
|
|
30
18
|
</label>
|
|
31
19
|
);
|
|
32
20
|
};
|
|
33
|
-
|
|
34
|
-
export type LabelOptionalProps = PropsWithChildren<CommonProps>;
|
|
35
|
-
|
|
36
|
-
const Optional = ({ children, className }: LabelOptionalProps) => {
|
|
37
|
-
const { formatMessage } = useIntl();
|
|
38
|
-
return (
|
|
39
|
-
<div>
|
|
40
|
-
{children}
|
|
41
|
-
<Body
|
|
42
|
-
as="span"
|
|
43
|
-
aria-label={formatMessage(messages.optionalAriaLabel)}
|
|
44
|
-
className={clsx('text-secondary', 'm-l-1', className)}
|
|
45
|
-
>
|
|
46
|
-
{formatMessage(messages.optionalLabel)}
|
|
47
|
-
</Body>
|
|
48
|
-
</div>
|
|
49
|
-
);
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
export type LabelDescriptionProps = PropsWithChildren<CommonProps>;
|
|
53
|
-
|
|
54
|
-
const Description = ({ children, className }: LabelDescriptionProps) =>
|
|
55
|
-
children ? <Body className={clsx('text-secondary', 'm-b-1', className)}>{children}</Body> : null;
|
|
56
|
-
|
|
57
|
-
Label.Optional = Optional;
|
|
58
|
-
Label.Description = Description;
|
|
59
|
-
|
|
60
|
-
export { Label };
|
package/src/main.css
CHANGED
|
@@ -1719,10 +1719,18 @@ button.np-option {
|
|
|
1719
1719
|
white-space: nowrap;
|
|
1720
1720
|
width: 100%;
|
|
1721
1721
|
}
|
|
1722
|
+
.np-date-trigger .control-label {
|
|
1723
|
+
font-weight: 400;
|
|
1724
|
+
font-weight: var(--font-weight-regular);
|
|
1725
|
+
}
|
|
1722
1726
|
.np-theme-personal .np-date-trigger {
|
|
1723
1727
|
padding-left: 16px;
|
|
1724
1728
|
padding-left: var(--size-16);
|
|
1725
1729
|
}
|
|
1730
|
+
.np-theme-personal .np-date-trigger .control-label + span {
|
|
1731
|
+
font-weight: 400;
|
|
1732
|
+
font-weight: var(--font-weight-regular);
|
|
1733
|
+
}
|
|
1726
1734
|
.clear-btn {
|
|
1727
1735
|
transition: color 0.15s ease-in-out;
|
|
1728
1736
|
color: #c9cbce;
|
|
@@ -2647,6 +2655,10 @@ html:not([dir="rtl"]) .np-flow-navigation--sm .np-flow-navigation__stepper {
|
|
|
2647
2655
|
height: auto;
|
|
2648
2656
|
}
|
|
2649
2657
|
}
|
|
2658
|
+
.np-select-input-listbox-container--virtualized {
|
|
2659
|
+
/* The wrapping element shrinks this as needed */
|
|
2660
|
+
height: 100vh;
|
|
2661
|
+
}
|
|
2650
2662
|
.np-select-input-listbox-container--has-group {
|
|
2651
2663
|
scroll-padding-top: 32px;
|
|
2652
2664
|
scroll-padding-top: var(--size-32);
|
|
@@ -2665,6 +2677,12 @@ html:not([dir="rtl"]) .np-flow-navigation--sm .np-flow-navigation__stepper {
|
|
|
2665
2677
|
outline: var(--ring-outline-color) solid var(--ring-outline-width);
|
|
2666
2678
|
outline-offset: var(--ring-outline-offset);
|
|
2667
2679
|
}
|
|
2680
|
+
.np-select-input-listbox-container--virtualized .np-select-input-listbox {
|
|
2681
|
+
/* Adopted from `VList` in virtua: https://github.com/inokawa/virtua/blob/7f6ed5b37df6b480d4ff350f3960067c5b3519d2/src/react/VList.tsx#L113-L116 */
|
|
2682
|
+
overflow-y: auto;
|
|
2683
|
+
contain: strict;
|
|
2684
|
+
height: 100%;
|
|
2685
|
+
}
|
|
2668
2686
|
.np-select-input-separator-item {
|
|
2669
2687
|
margin: 8px;
|
|
2670
2688
|
margin: var(--size-8);
|
|
@@ -12,6 +12,6 @@ describe('Select', () => {
|
|
|
12
12
|
<Select options={options} selected={options[0]} onChange={() => {}} />
|
|
13
13
|
</Field>,
|
|
14
14
|
);
|
|
15
|
-
expect(screen.getByLabelText(
|
|
15
|
+
expect(screen.getByLabelText('Currency')).toHaveTextContent('USD');
|
|
16
16
|
});
|
|
17
17
|
});
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var reactIntl = require('react-intl');
|
|
4
|
-
|
|
5
|
-
var messages = reactIntl.defineMessages({
|
|
6
|
-
optionalLabel: {
|
|
7
|
-
id: "neptune.Label.optional"
|
|
8
|
-
},
|
|
9
|
-
optionalAriaLabel: {
|
|
10
|
-
id: "neptune.aria.Label.optional"
|
|
11
|
-
}
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
module.exports = messages;
|
|
15
|
-
//# sourceMappingURL=Label.messages.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Label.messages.js","sources":["../../src/label/Label.messages.tsx"],"sourcesContent":["import { defineMessages } from 'react-intl';\n\nexport default defineMessages({\n optionalLabel: {\n id: 'neptune.Label.optional',\n defaultMessage: '(Optional)',\n },\n optionalAriaLabel: {\n id: 'neptune.aria.Label.optional',\n defaultMessage: 'This field is optional',\n },\n});\n"],"names":["defineMessages","optionalLabel","id","optionalAriaLabel"],"mappings":";;;;AAEA,eAAeA,wBAAc,CAAC;AAC5BC,EAAAA,aAAa,EAAE;IACbC,EAAE,EAAA,wBAAA;GAEH;AACDC,EAAAA,iBAAiB,EAAE;IACjBD,EAAE,EAAA,6BAAA;AAEH,GAAA;AACF,CAAA,CAAC;;;;"}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { defineMessages } from 'react-intl';
|
|
2
|
-
|
|
3
|
-
var messages = defineMessages({
|
|
4
|
-
optionalLabel: {
|
|
5
|
-
id: "neptune.Label.optional"
|
|
6
|
-
},
|
|
7
|
-
optionalAriaLabel: {
|
|
8
|
-
id: "neptune.aria.Label.optional"
|
|
9
|
-
}
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
export { messages as default };
|
|
13
|
-
//# sourceMappingURL=Label.messages.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Label.messages.mjs","sources":["../../src/label/Label.messages.tsx"],"sourcesContent":["import { defineMessages } from 'react-intl';\n\nexport default defineMessages({\n optionalLabel: {\n id: 'neptune.Label.optional',\n defaultMessage: '(Optional)',\n },\n optionalAriaLabel: {\n id: 'neptune.aria.Label.optional',\n defaultMessage: 'This field is optional',\n },\n});\n"],"names":["defineMessages","optionalLabel","id","optionalAriaLabel"],"mappings":";;AAEA,eAAeA,cAAc,CAAC;AAC5BC,EAAAA,aAAa,EAAE;IACbC,EAAE,EAAA,wBAAA;GAEH;AACDC,EAAAA,iBAAiB,EAAE;IACjBD,EAAE,EAAA,6BAAA;AAEH,GAAA;AACF,CAAA,CAAC;;;;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Label.messages.d.ts","sourceRoot":"","sources":["../../../src/label/Label.messages.tsx"],"names":[],"mappings":";;;;;;;;;;AAEA,wBASG"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/label/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,YAAY,EAAE,UAAU,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC"}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { defineMessages } from 'react-intl';
|
|
2
|
-
|
|
3
|
-
export default defineMessages({
|
|
4
|
-
optionalLabel: {
|
|
5
|
-
id: 'neptune.Label.optional',
|
|
6
|
-
defaultMessage: '(Optional)',
|
|
7
|
-
},
|
|
8
|
-
optionalAriaLabel: {
|
|
9
|
-
id: 'neptune.aria.Label.optional',
|
|
10
|
-
defaultMessage: 'This field is optional',
|
|
11
|
-
},
|
|
12
|
-
});
|
package/src/label/index.ts
DELETED