@bspk/ui 1.3.9 → 1.3.11
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/dist/components/BannerAlert/BannerAlert.d.ts +5 -5
- package/dist/components/BannerAlert/BannerAlert.js +5 -5
- package/dist/components/Breadcrumb/BreadcrumbDropdown.d.ts +6 -0
- package/dist/components/Breadcrumb/BreadcrumbDropdown.js +6 -0
- package/dist/components/Breadcrumb/BreadcrumbDropdown.js.map +1 -1
- package/dist/components/CheckboxGroup/CheckboxGroupExample.js +1 -0
- package/dist/components/CheckboxGroup/CheckboxGroupExample.js.map +1 -1
- package/dist/components/ChipGroup/ChipGroup.d.ts +15 -28
- package/dist/components/ChipGroup/ChipGroup.js +12 -22
- package/dist/components/ChipGroup/ChipGroup.js.map +1 -1
- package/dist/components/ChipGroup/ChipGroupExample.js +61 -6
- package/dist/components/ChipGroup/ChipGroupExample.js.map +1 -1
- package/dist/components/ChipGroup/chip-group.css +5 -3
- package/dist/components/ChipGroup/chip-group.css.js +5 -3
- package/dist/components/Drawer/Drawer.js.map +1 -1
- package/dist/components/Field/FieldDescription.d.ts +7 -5
- package/dist/components/Field/FieldDescription.js +7 -3
- package/dist/components/Field/FieldDescription.js.map +1 -1
- package/dist/components/Field/FieldError.d.ts +6 -0
- package/dist/components/Field/FieldError.js +6 -0
- package/dist/components/Field/FieldError.js.map +1 -1
- package/dist/components/Field/FieldLabel.d.ts +6 -0
- package/dist/components/Field/FieldLabel.js +6 -0
- package/dist/components/Field/FieldLabel.js.map +1 -1
- package/dist/components/Field/utils.d.ts +5 -0
- package/dist/components/Field/utils.js +5 -0
- package/dist/components/Field/utils.js.map +1 -1
- package/dist/components/InlineAlert/SvgWarningTwoTone.d.ts +6 -0
- package/dist/components/InlineAlert/SvgWarningTwoTone.js +6 -0
- package/dist/components/InlineAlert/SvgWarningTwoTone.js.map +1 -1
- package/dist/components/InputNumber/IncrementButton.d.ts +13 -3
- package/dist/components/InputNumber/IncrementButton.js +11 -4
- package/dist/components/InputNumber/IncrementButton.js.map +1 -1
- package/dist/components/InputNumber/InputNumber.js +26 -10
- package/dist/components/InputNumber/InputNumber.js.map +1 -1
- package/dist/components/InputNumber/InputNumberExample.js +1 -0
- package/dist/components/InputNumber/InputNumberExample.js.map +1 -1
- package/dist/components/InputNumber/input-number.css +6 -0
- package/dist/components/InputNumber/input-number.css.js +6 -0
- package/dist/components/Link/Link.d.ts +1 -1
- package/dist/components/Link/Link.js +1 -1
- package/dist/components/OTPInput/OTPInput.d.ts +13 -3
- package/dist/components/OTPInput/OTPInput.js +11 -39
- package/dist/components/OTPInput/OTPInput.js.map +1 -1
- package/dist/components/OTPInput/OTPInputExample.js +6 -1
- package/dist/components/OTPInput/OTPInputExample.js.map +1 -1
- package/dist/components/OTPInput/otp-input.css +18 -17
- package/dist/components/OTPInput/otp-input.css.js +18 -17
- package/dist/components/Pagination/PageList.d.ts +6 -0
- package/dist/components/Pagination/PageList.js +6 -0
- package/dist/components/Pagination/PageList.js.map +1 -1
- package/dist/components/Scrim/Scrim.d.ts +0 -1
- package/dist/components/Scrim/Scrim.js +0 -1
- package/dist/components/Scrim/Scrim.js.map +1 -1
- package/dist/components/Select/Select.d.ts +11 -11
- package/dist/components/Select/Select.js +11 -11
- package/dist/components/Skeleton/Circular.d.ts +6 -0
- package/dist/components/Skeleton/Circular.js +6 -0
- package/dist/components/Skeleton/Circular.js.map +1 -1
- package/dist/components/Skeleton/Photo.d.ts +6 -0
- package/dist/components/Skeleton/Photo.js +6 -0
- package/dist/components/Skeleton/Photo.js.map +1 -1
- package/dist/components/Skeleton/Profile.d.ts +6 -0
- package/dist/components/Skeleton/Profile.js +6 -0
- package/dist/components/Skeleton/Profile.js.map +1 -1
- package/dist/components/Skeleton/Rectangular.d.ts +6 -0
- package/dist/components/Skeleton/Rectangular.js +6 -0
- package/dist/components/Skeleton/Rectangular.js.map +1 -1
- package/dist/components/Skeleton/Thumbnail.d.ts +6 -0
- package/dist/components/Skeleton/Thumbnail.js +6 -0
- package/dist/components/Skeleton/Thumbnail.js.map +1 -1
- package/dist/components/Slider/SliderIntervalDots.d.ts +6 -0
- package/dist/components/Slider/SliderIntervalDots.js +6 -0
- package/dist/components/Slider/SliderIntervalDots.js.map +1 -1
- package/dist/components/Snackbar/Manager.d.ts +0 -1
- package/dist/components/Snackbar/Manager.js +0 -1
- package/dist/components/Snackbar/Manager.js.map +1 -1
- package/dist/components/Snackbar/Snackbar.d.ts +0 -1
- package/dist/components/Snackbar/Snackbar.js +0 -1
- package/dist/components/Snackbar/Snackbar.js.map +1 -1
- package/dist/components/TabList/TabList.js +1 -2
- package/dist/components/TabList/TabList.js.map +1 -1
- package/dist/components/Table/Footer.d.ts +6 -0
- package/dist/components/Table/Footer.js +6 -0
- package/dist/components/Table/Footer.js.map +1 -1
- package/dist/components/TimePicker/Listbox.d.ts +6 -0
- package/dist/components/TimePicker/Listbox.js +6 -0
- package/dist/components/TimePicker/Listbox.js.map +1 -1
- package/dist/components/TimePicker/Segment.d.ts +6 -0
- package/dist/components/TimePicker/Segment.js +6 -0
- package/dist/components/TimePicker/Segment.js.map +1 -1
- package/dist/components/Truncated/Truncated.d.ts +0 -1
- package/dist/components/Truncated/Truncated.js +1 -2
- package/dist/components/Truncated/Truncated.js.map +1 -1
- package/dist/components/UIProvider/UIProvider.d.ts +0 -1
- package/dist/components/UIProvider/UIProvider.js +0 -1
- package/dist/components/UIProvider/UIProvider.js.map +1 -1
- package/dist/hooks/useLongPress.d.ts +30 -15
- package/dist/hooks/useLongPress.js +26 -42
- package/dist/hooks/useLongPress.js.map +1 -1
- package/dist/styles/base.css +9 -0
- package/dist/styles/base.css.js +9 -0
- package/package.json +1 -1
- package/src/components/BannerAlert/BannerAlert.tsx +5 -5
- package/src/components/Breadcrumb/BreadcrumbDropdown.tsx +6 -0
- package/src/components/CheckboxGroup/CheckboxGroupExample.tsx +1 -0
- package/src/components/ChipGroup/ChipGroup.rtl.test.tsx +16 -11
- package/src/components/ChipGroup/ChipGroup.tsx +18 -36
- package/src/components/ChipGroup/ChipGroupExample.tsx +64 -36
- package/src/components/ChipGroup/chip-group.scss +5 -3
- package/src/components/Drawer/Drawer.tsx +0 -1
- package/src/components/Field/FieldDescription.tsx +7 -5
- package/src/components/Field/FieldError.tsx +6 -0
- package/src/components/Field/FieldLabel.tsx +6 -0
- package/src/components/Field/utils.ts +5 -0
- package/src/components/InlineAlert/SvgWarningTwoTone.tsx +6 -0
- package/src/components/InputNumber/IncrementButton.tsx +21 -11
- package/src/components/InputNumber/InputNumber.tsx +33 -31
- package/src/components/InputNumber/InputNumberExample.tsx +1 -0
- package/src/components/InputNumber/input-number.scss +10 -0
- package/src/components/Link/Link.tsx +1 -1
- package/src/components/OTPInput/OTPInput.rtl.test.tsx +4 -2
- package/src/components/OTPInput/OTPInput.tsx +34 -63
- package/src/components/OTPInput/OTPInputExample.tsx +6 -1
- package/src/components/OTPInput/otp-input.scss +50 -45
- package/src/components/Pagination/PageList.tsx +6 -0
- package/src/components/Scrim/Scrim.tsx +0 -1
- package/src/components/Select/Select.tsx +11 -11
- package/src/components/Skeleton/Circular.tsx +6 -0
- package/src/components/Skeleton/Photo.tsx +6 -0
- package/src/components/Skeleton/Profile.tsx +6 -0
- package/src/components/Skeleton/Rectangular.tsx +6 -0
- package/src/components/Skeleton/Thumbnail.tsx +6 -0
- package/src/components/Slider/SliderIntervalDots.tsx +6 -0
- package/src/components/Snackbar/Manager.tsx +0 -1
- package/src/components/Snackbar/Snackbar.tsx +0 -1
- package/src/components/TabList/TabList.tsx +1 -2
- package/src/components/Table/Footer.tsx +6 -0
- package/src/components/TimePicker/Listbox.tsx +6 -0
- package/src/components/TimePicker/Segment.tsx +6 -0
- package/src/components/Truncated/Truncated.tsx +1 -2
- package/src/components/UIProvider/UIProvider.tsx +0 -1
- package/src/hooks/useLongPress.ts +58 -48
- package/src/styles/base.scss +9 -0
- package/dist/components/Truncated/truncated.css +0 -8
- package/dist/components/Truncated/truncated.css.js +0 -13
- package/src/components/Truncated/truncated.scss +0 -8
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { ChipGroup } from './ChipGroup';
|
|
2
2
|
import { presets } from './ChipGroupExample';
|
|
3
|
-
import { Chip } from '-/components/Chip';
|
|
4
3
|
import { hasNoBasicA11yIssues } from '-/rtl/hasNoBasicA11yIssues';
|
|
5
4
|
import { render } from '-/rtl/util';
|
|
6
5
|
|
|
@@ -9,22 +8,28 @@ describe('ChipGroup (RTL)', () => {
|
|
|
9
8
|
it(
|
|
10
9
|
`has no basic a11y issues - ${preset.label}`,
|
|
11
10
|
hasNoBasicA11yIssues(
|
|
12
|
-
<ChipGroup
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
<ChipGroup
|
|
12
|
+
{...preset.propState}
|
|
13
|
+
items={[
|
|
14
|
+
{ label: 'suggestion 1', onClick: () => {} },
|
|
15
|
+
{ label: 'suggestion 2', onClick: () => {} },
|
|
16
|
+
{ label: 'suggestion 3', onClick: () => {} },
|
|
17
|
+
]}
|
|
18
|
+
/>,
|
|
17
19
|
),
|
|
18
20
|
);
|
|
19
21
|
});
|
|
20
22
|
|
|
21
23
|
it('renders', () => {
|
|
22
24
|
const { getByText } = render(
|
|
23
|
-
<ChipGroup
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
<ChipGroup
|
|
26
|
+
{...presets[1].propState}
|
|
27
|
+
items={[
|
|
28
|
+
{ label: 'suggestion 1', onClick: () => {} },
|
|
29
|
+
{ label: 'suggestion 2', onClick: () => {} },
|
|
30
|
+
{ label: 'suggestion 3', onClick: () => {} },
|
|
31
|
+
]}
|
|
32
|
+
/>,
|
|
28
33
|
);
|
|
29
34
|
|
|
30
35
|
expect(getByText('suggestion 1')).toBeInTheDocument();
|
|
@@ -1,23 +1,17 @@
|
|
|
1
1
|
import './chip-group.scss';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
import { ChipProps } from '-/components/Chip';
|
|
6
|
-
|
|
7
|
-
export type ChipGroupItem = Pick<
|
|
8
|
-
ChipProps,
|
|
9
|
-
'disabled' | 'flat' | 'label' | 'leadingIcon' | 'onClick' | 'selected' | 'trailingBadge' | 'trailingIcon'
|
|
10
|
-
>;
|
|
3
|
+
import { Chip, ChipProps } from '-/components/Chip';
|
|
11
4
|
|
|
12
5
|
export type ChipGroupProps = {
|
|
13
6
|
/**
|
|
14
|
-
*
|
|
7
|
+
* Controls the overflow behavior of the chip group. If set to `scroll`, the chip group will be scrollable
|
|
8
|
+
* horizontally. If set to `wrap`, the chip group will wrap to multiple lines as needed.
|
|
15
9
|
*
|
|
16
|
-
* @default
|
|
10
|
+
* @default wrap
|
|
17
11
|
*/
|
|
18
|
-
|
|
19
|
-
/** Only Chip components should be used as
|
|
20
|
-
|
|
12
|
+
overflow?: 'scroll' | 'wrap';
|
|
13
|
+
/** Only Chip components should be used as items. */
|
|
14
|
+
items?: ChipProps[];
|
|
21
15
|
};
|
|
22
16
|
/**
|
|
23
17
|
* A component that manages the layout of a group of chips.
|
|
@@ -26,34 +20,22 @@ export type ChipGroupProps = {
|
|
|
26
20
|
* import { Chip } from '@bspk/ui/Chip';
|
|
27
21
|
* import { ChipGroup } from '@bspk/ui/ChipGroup';
|
|
28
22
|
*
|
|
29
|
-
* <ChipGroup
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* leadingIcon
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
* label="chip 2"
|
|
38
|
-
* leadingIcon={<SvgIcecream />}
|
|
39
|
-
* onClick={() => action('Chip clicked!')}
|
|
40
|
-
* trailingIcon={<SvgChevronRight />}
|
|
41
|
-
* />
|
|
42
|
-
* <Chip
|
|
43
|
-
* label="chip 3"
|
|
44
|
-
* leadingIcon={<SvgSignLanguage />}
|
|
45
|
-
* onClick={() => action('Chip clicked!')}
|
|
46
|
-
* trailingIcon={<SvgClose />}
|
|
47
|
-
* />
|
|
48
|
-
* </ChipGroup>;
|
|
23
|
+
* <ChipGroup
|
|
24
|
+
* overflow="scroll"
|
|
25
|
+
* items={[
|
|
26
|
+
* { label: 'chip 1', leadingIcon: <SvgLightbulb />, onClick: () => {}, trailingIcon: <SvgChevronRight /> },
|
|
27
|
+
* { label: 'chip 2', leadingIcon: <SvgIcecream />, onClick: () => {}, trailingIcon: <SvgChevronRight /> },
|
|
28
|
+
* { label: 'chip 3', leadingIcon: <SvgSignLanguage />, onClick: () => {}, trailingIcon: <SvgClose /> },
|
|
29
|
+
* ]}
|
|
30
|
+
* />;
|
|
49
31
|
*
|
|
50
32
|
* @name ChipGroup
|
|
51
33
|
* @phase UXReview
|
|
52
34
|
*/
|
|
53
|
-
export function ChipGroup({
|
|
35
|
+
export function ChipGroup({ overflow = 'wrap', items }: ChipGroupProps) {
|
|
54
36
|
return (
|
|
55
|
-
<div data-bspk="chip-group" data-
|
|
56
|
-
{
|
|
37
|
+
<div data-bspk="chip-group" data-scroll={overflow === 'scroll' || undefined}>
|
|
38
|
+
{items?.length ? items.map((item, idx) => <Chip {...item} key={item.label ?? idx} />) : null}
|
|
57
39
|
</div>
|
|
58
40
|
);
|
|
59
41
|
}
|
|
@@ -7,53 +7,81 @@ import { SvgLightbulb } from '@bspk/icons/Lightbulb';
|
|
|
7
7
|
import { SvgOpportunities } from '@bspk/icons/Opportunities';
|
|
8
8
|
import { SvgSignLanguage } from '@bspk/icons/SignLanguage';
|
|
9
9
|
|
|
10
|
-
import { Chip } from '-/components/Chip';
|
|
10
|
+
// import { Chip } from '-/components/Chip';
|
|
11
11
|
import { ChipGroupProps } from '-/components/ChipGroup';
|
|
12
12
|
import { ComponentExampleFn, Preset } from '-/utils/demo';
|
|
13
13
|
|
|
14
14
|
export const presets: Preset<ChipGroupProps>[] = [
|
|
15
|
-
{
|
|
16
|
-
|
|
15
|
+
{
|
|
16
|
+
label: 'Scroll',
|
|
17
|
+
propState: {
|
|
18
|
+
overflow: 'scroll',
|
|
19
|
+
items: [
|
|
20
|
+
{ label: 'chip 1', leadingIcon: <SvgLightbulb />, trailingIcon: <SvgChevronRight /> },
|
|
21
|
+
{ label: 'chip 2', leadingIcon: <SvgIcecream />, trailingIcon: <SvgChevronRight /> },
|
|
22
|
+
{ label: 'chip 3', leadingIcon: <SvgSignLanguage />, trailingIcon: <SvgClose /> },
|
|
23
|
+
{ label: 'chip 4', leadingIcon: <SvgOpportunities />, trailingIcon: <SvgClose /> },
|
|
24
|
+
{ label: 'chip 5', leadingIcon: <SvgCloud />, trailingIcon: <SvgKeyboardArrowDown /> },
|
|
25
|
+
],
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
label: 'Scroll: Flat chips',
|
|
30
|
+
propState: {
|
|
31
|
+
overflow: 'scroll',
|
|
32
|
+
items: [
|
|
33
|
+
{
|
|
34
|
+
flat: true,
|
|
35
|
+
label: 'chip 1',
|
|
36
|
+
leadingIcon: <SvgLightbulb />,
|
|
37
|
+
trailingBadge: { count: 9, size: 'x-small' },
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
flat: true,
|
|
41
|
+
label: 'chip 2',
|
|
42
|
+
leadingIcon: <SvgIcecream />,
|
|
43
|
+
trailingBadge: { count: 2, size: 'x-small' },
|
|
44
|
+
},
|
|
45
|
+
{ flat: true, label: 'chip 3', leadingIcon: <SvgSignLanguage />, trailingIcon: <SvgClose /> },
|
|
46
|
+
{
|
|
47
|
+
flat: true,
|
|
48
|
+
label: 'chip 4',
|
|
49
|
+
leadingIcon: <SvgOpportunities />,
|
|
50
|
+
trailingBadge: { count: 5, size: 'x-small' },
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
flat: true,
|
|
54
|
+
label: 'chip 5',
|
|
55
|
+
leadingIcon: <SvgCloud />,
|
|
56
|
+
trailingBadge: { count: 3, size: 'x-small' },
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
},
|
|
60
|
+
},
|
|
17
61
|
];
|
|
18
62
|
|
|
19
63
|
export const ChipGroupExample: ComponentExampleFn<ChipGroupProps> = ({ action }) => ({
|
|
20
64
|
containerStyle: { width: '600px' },
|
|
21
65
|
presets,
|
|
66
|
+
defaultState: {
|
|
67
|
+
overflow: 'wrap',
|
|
68
|
+
items: [
|
|
69
|
+
{ label: 'chip 1', leadingIcon: <SvgLightbulb />, trailingIcon: <SvgChevronRight /> },
|
|
70
|
+
{ label: 'chip 2', leadingIcon: <SvgIcecream />, trailingIcon: <SvgChevronRight /> },
|
|
71
|
+
{ label: 'chip 3', leadingIcon: <SvgSignLanguage />, trailingIcon: <SvgClose /> },
|
|
72
|
+
{ label: 'chip 4', leadingIcon: <SvgOpportunities />, trailingIcon: <SvgClose /> },
|
|
73
|
+
{ label: 'chip 5', leadingIcon: <SvgCloud />, trailingIcon: <SvgKeyboardArrowDown /> },
|
|
74
|
+
],
|
|
75
|
+
},
|
|
22
76
|
render: ({ props, Component }) => {
|
|
23
|
-
const handleChipInputClick = () => action('Chip clicked!');
|
|
24
77
|
return (
|
|
25
|
-
<Component
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
onClick
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
<Chip
|
|
33
|
-
label="chip 2"
|
|
34
|
-
leadingIcon={<SvgIcecream />}
|
|
35
|
-
onClick={handleChipInputClick}
|
|
36
|
-
trailingIcon={<SvgChevronRight />}
|
|
37
|
-
/>
|
|
38
|
-
<Chip
|
|
39
|
-
label="chip 3"
|
|
40
|
-
leadingIcon={<SvgSignLanguage />}
|
|
41
|
-
onClick={handleChipInputClick}
|
|
42
|
-
trailingIcon={<SvgClose />}
|
|
43
|
-
/>
|
|
44
|
-
<Chip
|
|
45
|
-
label="chip 4"
|
|
46
|
-
leadingIcon={<SvgOpportunities />}
|
|
47
|
-
onClick={handleChipInputClick}
|
|
48
|
-
trailingIcon={<SvgClose />}
|
|
49
|
-
/>
|
|
50
|
-
<Chip
|
|
51
|
-
label="chip 5"
|
|
52
|
-
leadingIcon={<SvgCloud />}
|
|
53
|
-
onClick={handleChipInputClick}
|
|
54
|
-
trailingIcon={<SvgKeyboardArrowDown />}
|
|
55
|
-
/>
|
|
56
|
-
</Component>
|
|
78
|
+
<Component
|
|
79
|
+
{...props}
|
|
80
|
+
items={props.items?.map((item) => ({
|
|
81
|
+
...item,
|
|
82
|
+
onClick: () => action('Chip clicked!'),
|
|
83
|
+
}))}
|
|
84
|
+
/>
|
|
57
85
|
);
|
|
58
86
|
},
|
|
59
87
|
});
|
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
display: flex;
|
|
3
3
|
gap: var(--spacing-sizing-02);
|
|
4
4
|
width: 100%;
|
|
5
|
-
|
|
5
|
+
flex-flow: row wrap;
|
|
6
|
+
padding-bottom: var(--spacing-sizing-01);
|
|
6
7
|
|
|
7
|
-
&[data-
|
|
8
|
-
|
|
8
|
+
&[data-scroll] {
|
|
9
|
+
overflow: auto;
|
|
10
|
+
flex-wrap: nowrap;
|
|
9
11
|
}
|
|
10
12
|
}
|
|
11
13
|
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { useFieldContext, describedById } from './utils';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* FieldDescription component displays a description associated with a form field.
|
|
5
|
+
*
|
|
6
|
+
* @name FieldDescription
|
|
7
|
+
* @parent Field
|
|
8
|
+
*/
|
|
9
|
+
export function FieldDescription({ children }: { children?: string }) {
|
|
4
10
|
const { id } = useFieldContext();
|
|
5
11
|
|
|
6
12
|
return children ? (
|
|
@@ -9,7 +15,3 @@ function FieldDescription({ children }: { children?: string }) {
|
|
|
9
15
|
</p>
|
|
10
16
|
) : null;
|
|
11
17
|
}
|
|
12
|
-
|
|
13
|
-
FieldDescription.displayName = 'FieldDescription';
|
|
14
|
-
|
|
15
|
-
export { FieldDescription };
|
|
@@ -6,6 +6,12 @@ export type FieldErrorProps = {
|
|
|
6
6
|
children?: string;
|
|
7
7
|
};
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* FieldError component displays an error message associated with a form field.
|
|
11
|
+
*
|
|
12
|
+
* @name FieldError
|
|
13
|
+
* @parent Field
|
|
14
|
+
*/
|
|
9
15
|
export function FieldError({ children }: FieldErrorProps) {
|
|
10
16
|
const { id } = useFieldContext();
|
|
11
17
|
|
|
@@ -14,6 +14,12 @@ export type FieldLabelProps<As extends ElementType = ElementType> = Pick<FieldCo
|
|
|
14
14
|
as?: As;
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* FieldLabel component displays a label associated with a form field.
|
|
19
|
+
*
|
|
20
|
+
* @name FieldLabel
|
|
21
|
+
* @parent Field
|
|
22
|
+
*/
|
|
17
23
|
export function FieldLabel<As extends ElementType = ElementType>({
|
|
18
24
|
children,
|
|
19
25
|
labelTrailing,
|
|
@@ -28,6 +28,11 @@ export type FieldContext = FieldContextProps & {
|
|
|
28
28
|
|
|
29
29
|
export const fieldContext = createContext<FieldContext | null>(null);
|
|
30
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Retrieves the current Field context.
|
|
33
|
+
*
|
|
34
|
+
* Will return a default context if used outside of a Field component.
|
|
35
|
+
*/
|
|
31
36
|
export function useFieldContext(): FieldContext {
|
|
32
37
|
return (
|
|
33
38
|
useContext(fieldContext) || {
|
|
@@ -3,29 +3,39 @@ import { SvgRemove } from '@bspk/icons/Remove';
|
|
|
3
3
|
import { useLongPress } from '-/hooks/useLongPress';
|
|
4
4
|
|
|
5
5
|
export type IncrementButtonProps = {
|
|
6
|
+
/** Whether the button is disabled. */
|
|
6
7
|
disabled: boolean;
|
|
7
|
-
increment
|
|
8
|
-
|
|
8
|
+
/** The kind of increment button, either 'add' or 'remove'. */
|
|
9
|
+
kind: 'add' | 'remove';
|
|
10
|
+
/** The ID of the associated input element. */
|
|
9
11
|
inputId: string;
|
|
12
|
+
/** Function to trigger the increment action. */
|
|
13
|
+
triggerIncrement: (kind: 'add' | 'remove') => boolean;
|
|
10
14
|
};
|
|
11
15
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
/**
|
|
17
|
+
* A button component for incrementing or decrementing the InputNumber.
|
|
18
|
+
*
|
|
19
|
+
* @name IncrementButton
|
|
20
|
+
* @parent InputNumber
|
|
21
|
+
*/
|
|
22
|
+
export function IncrementButton({ inputId, kind, disabled, triggerIncrement }: IncrementButtonProps) {
|
|
23
|
+
const { ...pressHandlers } = useLongPress({
|
|
24
|
+
callback: () => triggerIncrement(kind),
|
|
25
|
+
});
|
|
16
26
|
|
|
17
27
|
return (
|
|
18
28
|
<button
|
|
19
|
-
{...
|
|
29
|
+
{...pressHandlers}
|
|
20
30
|
aria-controls={inputId}
|
|
21
|
-
aria-
|
|
22
|
-
data-
|
|
31
|
+
aria-label={kind === 'add' ? 'Increase value' : 'Decrease value'}
|
|
32
|
+
data-bspk="input-number--increment-button"
|
|
33
|
+
data-kind={kind}
|
|
23
34
|
disabled={disabled}
|
|
24
|
-
ref={setTriggerElement}
|
|
25
35
|
tabIndex={-1}
|
|
26
36
|
type="button"
|
|
27
37
|
>
|
|
28
|
-
{add ? <SvgAdd /> : <SvgRemove />}
|
|
38
|
+
{kind === 'add' ? <SvgAdd aria-hidden /> : <SvgRemove aria-hidden />}
|
|
29
39
|
</button>
|
|
30
40
|
);
|
|
31
41
|
}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import './input-number.scss';
|
|
2
|
-
import {
|
|
2
|
+
import { useEffect, useRef } from 'react';
|
|
3
3
|
import { IncrementButton } from './IncrementButton';
|
|
4
4
|
import { useFieldInit } from '-/components/Field';
|
|
5
5
|
import { useId } from '-/hooks/useId';
|
|
6
6
|
import { CommonProps, FieldControlProps } from '-/types/common';
|
|
7
7
|
|
|
8
|
-
function isNumber(value: unknown
|
|
8
|
+
function isNumber(value: unknown): number | undefined;
|
|
9
|
+
function isNumber(value: unknown, fallbackValue: number): number;
|
|
10
|
+
function isNumber(value: unknown, fallbackValue?: number): number | undefined {
|
|
9
11
|
if (typeof value === 'number') return value;
|
|
10
12
|
if (typeof value !== 'string') return fallbackValue;
|
|
11
|
-
const num =
|
|
13
|
+
const num = parseFloat(value);
|
|
12
14
|
return isNaN(num) ? fallbackValue : num;
|
|
13
15
|
}
|
|
14
16
|
|
|
@@ -108,14 +110,25 @@ export function InputNumber({
|
|
|
108
110
|
readOnly,
|
|
109
111
|
invalidProp,
|
|
110
112
|
});
|
|
111
|
-
|
|
112
113
|
const max = typeof maxProp === 'number' && maxProp >= min ? maxProp : Number.MAX_SAFE_INTEGER;
|
|
113
114
|
const centered = align !== 'left';
|
|
114
115
|
const inputId = useId(id);
|
|
115
|
-
const value =
|
|
116
|
+
const value = isNumber(valueProp, min);
|
|
117
|
+
const removeDisabled = disabled || value + step * -1 < min;
|
|
118
|
+
const addDisabled = disabled || value + step > max;
|
|
119
|
+
|
|
120
|
+
const valueRef = useRef(value);
|
|
116
121
|
|
|
117
|
-
|
|
118
|
-
|
|
122
|
+
useEffect(() => {
|
|
123
|
+
valueRef.current = value;
|
|
124
|
+
}, [value]);
|
|
125
|
+
|
|
126
|
+
const incrementHandler = (kind: 'add' | 'remove') => {
|
|
127
|
+
const increment = kind === 'add' ? step : step * -1;
|
|
128
|
+
const next = valueRef.current + increment;
|
|
129
|
+
if (next < min || next > max) return false;
|
|
130
|
+
onChange(next);
|
|
131
|
+
return true;
|
|
119
132
|
};
|
|
120
133
|
|
|
121
134
|
return (
|
|
@@ -128,14 +141,6 @@ export function InputNumber({
|
|
|
128
141
|
data-size={size}
|
|
129
142
|
data-stepper-input
|
|
130
143
|
>
|
|
131
|
-
{!!centered && (
|
|
132
|
-
<IncrementButton
|
|
133
|
-
disabled={disabled ? true : value + -1 < min}
|
|
134
|
-
increment={-1}
|
|
135
|
-
inputId={inputId}
|
|
136
|
-
onIncrement={handleIncrement}
|
|
137
|
-
/>
|
|
138
|
-
)}
|
|
139
144
|
<input
|
|
140
145
|
{...inputElementProps}
|
|
141
146
|
aria-describedby={ariaDescribedBy || undefined}
|
|
@@ -151,32 +156,29 @@ export function InputNumber({
|
|
|
151
156
|
max={max}
|
|
152
157
|
min={min}
|
|
153
158
|
name={name}
|
|
159
|
+
onBlur={(e) => {
|
|
160
|
+
const next = isNumber(e.target.value, min);
|
|
161
|
+
e.target.value = next?.toString() || '';
|
|
162
|
+
onChange(next);
|
|
163
|
+
}}
|
|
154
164
|
onChange={(e) => {
|
|
155
|
-
|
|
165
|
+
const next = isNumber(e.target.value, min);
|
|
166
|
+
onChange(next);
|
|
156
167
|
}}
|
|
157
168
|
readOnly={readOnly}
|
|
158
169
|
required={required}
|
|
159
170
|
step={step}
|
|
160
171
|
type="number"
|
|
161
|
-
value={value}
|
|
172
|
+
value={value !== undefined ? value : ''}
|
|
162
173
|
/>
|
|
163
|
-
|
|
164
|
-
<>
|
|
165
|
-
<div aria-hidden data-divider />
|
|
166
|
-
<IncrementButton
|
|
167
|
-
disabled={!!disabled || value + -1 < min}
|
|
168
|
-
increment={-1}
|
|
169
|
-
inputId={inputId}
|
|
170
|
-
onIncrement={handleIncrement}
|
|
171
|
-
/>
|
|
172
|
-
</>
|
|
173
|
-
)}
|
|
174
|
+
<div aria-hidden data-divider />
|
|
174
175
|
<IncrementButton
|
|
175
|
-
disabled={
|
|
176
|
-
increment={1}
|
|
176
|
+
disabled={removeDisabled}
|
|
177
177
|
inputId={inputId}
|
|
178
|
-
|
|
178
|
+
kind="remove"
|
|
179
|
+
triggerIncrement={incrementHandler}
|
|
179
180
|
/>
|
|
181
|
+
<IncrementButton disabled={addDisabled} inputId={inputId} kind="add" triggerIncrement={incrementHandler} />
|
|
180
182
|
</div>
|
|
181
183
|
);
|
|
182
184
|
}
|
|
@@ -129,6 +129,16 @@
|
|
|
129
129
|
--height: var(--spacing-sizing-12);
|
|
130
130
|
--svg-width: var(--spacing-sizing-06);
|
|
131
131
|
}
|
|
132
|
+
|
|
133
|
+
&[data-centered] {
|
|
134
|
+
button:first-of-type {
|
|
135
|
+
order: -1;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
[data-divider] {
|
|
139
|
+
display: none;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
132
142
|
}
|
|
133
143
|
|
|
134
144
|
/** Copyright 2025 Anywhere Real Estate - CC BY 4.0 */
|
|
@@ -46,7 +46,7 @@ export type LinkProps = Pick<CommonPropsLibrary, 'disabled'> & {
|
|
|
46
46
|
* @example
|
|
47
47
|
* import { Link } from '@bspk/ui/Link';
|
|
48
48
|
*
|
|
49
|
-
* <Link href="https://
|
|
49
|
+
* <Link href="https://anywhere.re" label="Example label" trailingIcon="external" />;
|
|
50
50
|
*
|
|
51
51
|
* @name Link
|
|
52
52
|
* @phase UXReview
|
|
@@ -12,8 +12,10 @@ describe('OTPInput (RTL)', () => {
|
|
|
12
12
|
});
|
|
13
13
|
|
|
14
14
|
it('renders', () => {
|
|
15
|
-
const { getByLabelText } = render(
|
|
15
|
+
const { getByLabelText } = render(
|
|
16
|
+
<OTPInput aria-label="OTP input" onChange={() => {}} {...presets[0].propState} />,
|
|
17
|
+
);
|
|
16
18
|
|
|
17
|
-
expect(getByLabelText('OTP
|
|
19
|
+
expect(getByLabelText('OTP input')).toBeInTheDocument();
|
|
18
20
|
});
|
|
19
21
|
});
|