@nuskin/foundation-ui-components 1.1.1 → 1.3.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/package.json +5 -5
- package/src/common/auth-dropdown/NsAuthDropdown.tsx +16 -7
- package/src/common/autocomplete/NsAutocomplete.spec.tsx +3 -3
- package/src/common/autocomplete/mock/MockData.ts +1 -0
- package/src/common/button/NsButton.stories.tsx +0 -1
- package/src/common/card/styled/NsFfgStyled.tsx +0 -1
- package/src/common/carousal/NsCarousal.tsx +9 -3
- package/src/common/cart-icon/NsCartIcon.spec.tsx +5 -4
- package/src/common/cart-icon/NsCartIcon.stories.tsx +1 -1
- package/src/common/cart-icon/NsCartIcon.tsx +1 -1
- package/src/common/cart-icon/types.d.ts +2 -2
- package/src/common/checkbox/types.d.ts +1 -1
- package/src/common/dropdown/NsDropdown.tsx +17 -8
- package/src/common/hooks/useOnClickOutside.tsx +28 -0
- package/src/common/icon/NsIcon.spec.tsx +11 -5
- package/src/common/icon/NsIconVariants.ts +3 -1
- package/src/common/icon/icons/AlertErrorIcon.tsx +2 -1
- package/src/common/icon/icons/ArrowLeft.tsx +2 -2
- package/src/common/icon/icons/ArrowRight.tsx +2 -2
- package/src/common/icon/icons/CartBadge.tsx +2 -2
- package/src/common/icon/icons/Check.tsx +25 -0
- package/src/common/icon/icons/CloseIcon.tsx +1 -1
- package/src/common/icon/icons/ErrorIcon.tsx +3 -2
- package/src/common/icon/icons/SearchIcon.tsx +3 -2
- package/src/common/icon/icons/SuccessIcon.tsx +7 -1
- package/src/common/icon/icons/TrashIcon.tsx +2 -1
- package/src/common/icon/icons/UserOutline.tsx +4 -1
- package/src/common/icon/icons/WarningIcon.tsx +3 -2
- package/src/common/index.ts +8 -4
- package/src/common/label/NsLabel.stories.tsx +0 -3
- package/src/common/label/NsLabel.tsx +1 -1
- package/src/common/progress/NsProgress.spec.tsx +47 -0
- package/src/common/progress/NsProgress.stories.tsx +37 -0
- package/src/common/progress/NsProgress.styled.tsx +39 -0
- package/src/common/progress/NsProgress.tsx +27 -0
- package/src/common/progress/index.ts +2 -0
- package/src/common/progress/types.d.ts +21 -0
- package/src/common/radiogroup/NsRadioGroup.styled.tsx +4 -3
- package/src/common/select/NsSelect.spec.tsx +98 -17
- package/src/common/select/NsSelect.styled.tsx +52 -22
- package/src/common/select/NsSelect.tsx +109 -44
- package/src/common/select/NsSelectOption.tsx +0 -1
- package/src/common/select/stories/NsSelect.stories.tsx +23 -14
- package/src/common/select/types.d.ts +3 -1
|
@@ -1,8 +1,17 @@
|
|
|
1
|
-
import React, { useState, useRef, useEffect } from 'react';
|
|
2
|
-
import {
|
|
1
|
+
import React, { useState, useRef, useEffect, Children } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
SelectContainer,
|
|
4
|
+
SelectButton,
|
|
5
|
+
SelectList,
|
|
6
|
+
SelectHead,
|
|
7
|
+
SelectLabel,
|
|
8
|
+
SelectMain,
|
|
9
|
+
LeftArrowContainer,
|
|
10
|
+
RightArrowContainer,
|
|
11
|
+
ErrorContainer,
|
|
12
|
+
} from './NsSelect.styled';
|
|
3
13
|
import { NsIcon } from '../icon';
|
|
4
14
|
import { SelectProps } from './types.d';
|
|
5
|
-
|
|
6
15
|
const NsSelect: React.FC<SelectProps> = ({
|
|
7
16
|
selectedValue,
|
|
8
17
|
selectedLabel,
|
|
@@ -17,9 +26,14 @@ const NsSelect: React.FC<SelectProps> = ({
|
|
|
17
26
|
label,
|
|
18
27
|
readOnly = false,
|
|
19
28
|
errorMessage,
|
|
29
|
+
hasArrows,
|
|
30
|
+
// eslint-disable-next-line no-unused-vars
|
|
31
|
+
rules,
|
|
32
|
+
reverseArrowControls,
|
|
20
33
|
}) => {
|
|
21
34
|
const [isOpen, setIsOpen] = useState(false);
|
|
22
35
|
const [value, setValue] = useState(selectedValue || '');
|
|
36
|
+
const [selectedIndex, setSelectedIndex] = useState(-1);
|
|
23
37
|
const selectContainerRef = useRef<HTMLDivElement>(null);
|
|
24
38
|
const selectHeadRef = useRef<HTMLDivElement>(null);
|
|
25
39
|
|
|
@@ -29,19 +43,16 @@ const NsSelect: React.FC<SelectProps> = ({
|
|
|
29
43
|
}
|
|
30
44
|
};
|
|
31
45
|
|
|
32
|
-
const handleSelect = (input: string) => {
|
|
46
|
+
const handleSelect = (input: string, index: number) => {
|
|
33
47
|
onSelect(input);
|
|
34
48
|
setIsOpen(false);
|
|
35
49
|
setValue(input);
|
|
50
|
+
setSelectedIndex(index);
|
|
36
51
|
};
|
|
37
52
|
|
|
38
53
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
39
54
|
const handleClickOutside = (event: MouseEvent) => {
|
|
40
|
-
if (
|
|
41
|
-
selectContainerRef.current &&
|
|
42
|
-
!selectContainerRef.current.contains(event.target as Node) &&
|
|
43
|
-
isOpen
|
|
44
|
-
) {
|
|
55
|
+
if (selectContainerRef.current && !selectContainerRef.current.contains(event.target as Node) && isOpen) {
|
|
45
56
|
setIsOpen(false);
|
|
46
57
|
}
|
|
47
58
|
};
|
|
@@ -55,6 +66,40 @@ const NsSelect: React.FC<SelectProps> = ({
|
|
|
55
66
|
}, 0);
|
|
56
67
|
}
|
|
57
68
|
};
|
|
69
|
+
const handleArrowRightClick = () => {
|
|
70
|
+
if (selectedIndex !== -1) {
|
|
71
|
+
const nextIndex = reverseArrowControls ? selectedIndex - 1 : selectedIndex + 1;
|
|
72
|
+
const childrenArray = Children.toArray(children);
|
|
73
|
+
const optionAtIndex = childrenArray[nextIndex];
|
|
74
|
+
const { value } = (optionAtIndex as any)?.props ?? {};
|
|
75
|
+
handleSelect(value, nextIndex);
|
|
76
|
+
} else {
|
|
77
|
+
if (!reverseArrowControls) {
|
|
78
|
+
const nextIndex = selectedIndex + 1;
|
|
79
|
+
const childrenArray = Children.toArray(children);
|
|
80
|
+
const optionAtIndex = childrenArray[nextIndex];
|
|
81
|
+
const { value } = (optionAtIndex as any)?.props ?? {};
|
|
82
|
+
handleSelect(value, nextIndex);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
const handleArrowLeftClick = () => {
|
|
87
|
+
if (selectedIndex !== -1) {
|
|
88
|
+
const nextIndex = reverseArrowControls ? selectedIndex + 1 : selectedIndex - 1;
|
|
89
|
+
const childrenArray = Children.toArray(children);
|
|
90
|
+
const optionAtIndex = childrenArray[nextIndex];
|
|
91
|
+
const { value } = (optionAtIndex as any)?.props ?? {};
|
|
92
|
+
handleSelect(value, nextIndex);
|
|
93
|
+
} else {
|
|
94
|
+
if (reverseArrowControls) {
|
|
95
|
+
const nextIndex = selectedIndex + 1;
|
|
96
|
+
const childrenArray = Children.toArray(children);
|
|
97
|
+
const optionAtIndex = childrenArray[nextIndex];
|
|
98
|
+
const { value } = (optionAtIndex as any)?.props ?? {};
|
|
99
|
+
handleSelect(value, nextIndex);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
};
|
|
58
103
|
|
|
59
104
|
useEffect(() => {
|
|
60
105
|
document.addEventListener('click', handleClickOutside);
|
|
@@ -64,53 +109,73 @@ const NsSelect: React.FC<SelectProps> = ({
|
|
|
64
109
|
}, [handleClickOutside, isOpen]);
|
|
65
110
|
|
|
66
111
|
return (
|
|
67
|
-
<
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
112
|
+
<SelectMain>
|
|
113
|
+
{hasArrows && (
|
|
114
|
+
<LeftArrowContainer
|
|
115
|
+
onClick={handleArrowLeftClick}
|
|
116
|
+
className="LeftArrow"
|
|
117
|
+
style={{
|
|
118
|
+
visibility:
|
|
119
|
+
!reverseArrowControls && (selectedIndex === 0 || selectedIndex < 0) ? 'hidden' : 'visible',
|
|
120
|
+
}}
|
|
121
|
+
>
|
|
122
|
+
<NsIcon name="arrowLeft" color="primary" />
|
|
123
|
+
</LeftArrowContainer>
|
|
124
|
+
)}
|
|
125
|
+
<SelectContainer
|
|
126
|
+
data-testid="select-container"
|
|
79
127
|
disabled={disabled || readOnly}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
tabIndex={0}
|
|
83
|
-
ref={selectHeadRef}
|
|
128
|
+
ref={selectContainerRef}
|
|
129
|
+
errorMessage={errorMessage}
|
|
84
130
|
>
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
onMouseLeave={closeSelect}
|
|
94
|
-
borderColor={selectBorderColor}
|
|
95
|
-
className='selectList'
|
|
131
|
+
{label && <SelectLabel errorMessage={errorMessage}>{label}</SelectLabel>}
|
|
132
|
+
<SelectHead
|
|
133
|
+
data-testid="select-head"
|
|
134
|
+
disabled={disabled || readOnly}
|
|
135
|
+
type={type}
|
|
136
|
+
onMouseDown={handleMouseClick}
|
|
137
|
+
tabIndex={0}
|
|
138
|
+
ref={selectHeadRef}
|
|
96
139
|
>
|
|
97
|
-
<
|
|
98
|
-
|
|
140
|
+
<SelectButton>{selectedLabel || placeholder}</SelectButton>
|
|
141
|
+
{!readOnly && <NsIcon name={isOpen ? 'arrowUp' : 'arrowDown'} color="primary" />}
|
|
142
|
+
</SelectHead>
|
|
143
|
+
{isOpen ? (
|
|
144
|
+
<SelectList
|
|
145
|
+
data-testid="select-list"
|
|
146
|
+
onMouseLeave={closeSelect}
|
|
147
|
+
borderColor={selectBorderColor}
|
|
148
|
+
className="selectList"
|
|
149
|
+
>
|
|
150
|
+
{React.Children.map(children, (child, index) => {
|
|
99
151
|
if (React.isValidElement(child)) {
|
|
100
152
|
return React.cloneElement(child, {
|
|
101
153
|
// @ts-ignore
|
|
102
|
-
onSelect: handleSelect,
|
|
154
|
+
onSelect: (input: string) => handleSelect(input, index),
|
|
103
155
|
selectedValue: value,
|
|
104
156
|
showListDivider,
|
|
157
|
+
index,
|
|
105
158
|
});
|
|
106
159
|
}
|
|
107
160
|
return null;
|
|
108
161
|
})}
|
|
109
|
-
</
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
162
|
+
</SelectList>
|
|
163
|
+
) : null}
|
|
164
|
+
{errorMessage && <ErrorContainer>{errorMessage}</ErrorContainer>}
|
|
165
|
+
</SelectContainer>
|
|
166
|
+
{hasArrows && (
|
|
167
|
+
<RightArrowContainer
|
|
168
|
+
onClick={handleArrowRightClick}
|
|
169
|
+
className="RightArrow"
|
|
170
|
+
style={{
|
|
171
|
+
visibility:
|
|
172
|
+
reverseArrowControls && (selectedIndex === 0 || selectedIndex < 0) ? 'hidden' : 'visible',
|
|
173
|
+
}}
|
|
174
|
+
>
|
|
175
|
+
<NsIcon name="arrowRight" color="primary" />
|
|
176
|
+
</RightArrowContainer>
|
|
177
|
+
)}
|
|
178
|
+
</SelectMain>
|
|
114
179
|
);
|
|
115
180
|
};
|
|
116
181
|
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import React, { useState } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
3
3
|
import NsSelect from '../NsSelect';
|
|
4
4
|
import NsSelectOption from '../NsSelectOption';
|
|
5
|
+
import { withCenterDecorator } from '@/stories/decorators/withCenterDecorator';
|
|
6
|
+
|
|
7
|
+
type ComponentType = typeof NsSelect;
|
|
8
|
+
type Story = StoryObj<ComponentType>;
|
|
9
|
+
type MetaStory = Meta<ComponentType>;
|
|
5
10
|
|
|
6
11
|
export default {
|
|
7
12
|
title: 'Components/Inputs/NsSelect',
|
|
8
|
-
decorators: [
|
|
9
|
-
(story) => <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>{story()}</div>,
|
|
10
|
-
],
|
|
13
|
+
decorators: [withCenterDecorator],
|
|
11
14
|
component: NsSelect,
|
|
12
15
|
argTypes: {
|
|
13
16
|
type: { control: 'select', options: ['unfilled', 'outlined'] },
|
|
@@ -26,6 +29,7 @@ export default {
|
|
|
26
29
|
validateOnBlur: { control: 'boolean' },
|
|
27
30
|
autoComplete: { control: 'text' },
|
|
28
31
|
rules: { control: 'array' },
|
|
32
|
+
hasArrows: { control: 'boolean' },
|
|
29
33
|
},
|
|
30
34
|
parameters: {
|
|
31
35
|
options: [
|
|
@@ -100,17 +104,16 @@ export default {
|
|
|
100
104
|
validateOnBlur: false,
|
|
101
105
|
autoComplete: '',
|
|
102
106
|
rules: [],
|
|
107
|
+
hasArrows: false,
|
|
103
108
|
},
|
|
104
|
-
} as
|
|
105
|
-
|
|
106
|
-
export const Select: ComponentStory<typeof NsSelect> = (args, context) => {
|
|
107
|
-
const [selectedLabel, setSelectedLabel] = useState(args.selectedLabel);
|
|
108
|
-
const [selectedValue, setSelectedValue] = useState(args.selectedValue);
|
|
109
|
+
} as MetaStory;
|
|
109
110
|
|
|
110
|
-
|
|
111
|
+
const AppSelect = (props: any) => {
|
|
112
|
+
const [selectedLabel, setSelectedLabel] = useState(props.selectedLabel);
|
|
113
|
+
const [selectedValue, setSelectedValue] = useState(props.selectedValue);
|
|
111
114
|
const handleSelect = (value: string) => {
|
|
112
115
|
setSelectedValue(value);
|
|
113
|
-
options.find((option: any) => {
|
|
116
|
+
props.options.find((option: any) => {
|
|
114
117
|
if (option.value === value) {
|
|
115
118
|
setSelectedLabel(option.label);
|
|
116
119
|
}
|
|
@@ -120,15 +123,21 @@ export const Select: ComponentStory<typeof NsSelect> = (args, context) => {
|
|
|
120
123
|
|
|
121
124
|
return (
|
|
122
125
|
<NsSelect
|
|
123
|
-
{...
|
|
126
|
+
{...props}
|
|
124
127
|
selectedValue={selectedValue}
|
|
125
128
|
selectedLabel={selectedLabel}
|
|
126
129
|
onSelect={handleSelect}
|
|
127
|
-
label={
|
|
130
|
+
label={props.label}
|
|
128
131
|
>
|
|
129
|
-
{options.map((option: any) => (
|
|
132
|
+
{props.options.map((option: any) => (
|
|
130
133
|
<NsSelectOption key={option.value} value={option.value} label={option.label} />
|
|
131
134
|
))}
|
|
132
135
|
</NsSelect>
|
|
133
136
|
);
|
|
134
137
|
};
|
|
138
|
+
export const Select: Story = {
|
|
139
|
+
render: (args, context) => {
|
|
140
|
+
const options = context.parameters.options;
|
|
141
|
+
return <AppSelect options={options} {...args} />;
|
|
142
|
+
},
|
|
143
|
+
};
|
|
@@ -22,7 +22,9 @@ export interface SelectProps {
|
|
|
22
22
|
itemValue?: string;
|
|
23
23
|
validateOnBlur?: boolean;
|
|
24
24
|
autoComplete?: string;
|
|
25
|
-
|
|
25
|
+
// eslint-disable-next-line no-undef
|
|
26
|
+
rules?: Array;
|
|
27
|
+
hasArrows?: boolean;
|
|
26
28
|
}
|
|
27
29
|
|
|
28
30
|
export interface SelectOptionProps {
|