@nuskin/foundation-ui-components 1.3.0 → 1.4.1
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 +4 -4
- package/src/common/auth-dropdown/NsAuthDropdown.tsx +6 -6
- package/src/common/button/NsButton.stories.tsx +3 -2
- package/src/common/button/NsButton.tsx +27 -2
- package/src/common/button/types.d.ts +21 -17
- package/src/common/card/NsFfgCard.tsx +2 -2
- package/src/common/cart-icon/NsCartIcon.styled.tsx +14 -0
- package/src/common/dropdown/NsDropdown.tsx +6 -6
- package/src/common/email-simple/NsEmailSimple.tsx +1 -1
- package/src/common/flag/NsFlag.spec.tsx +14 -2
- package/src/common/flag/NsFlag.styled.tsx +2 -5
- package/src/common/hooks/useOnClickOutside.tsx +11 -14
- package/src/common/icon/NsIcon.spec.tsx +11 -2
- package/src/common/icon/NsIcon.stories.tsx +2 -1
- package/src/common/icon/NsIcon.styled.tsx +13 -10
- package/src/common/icon/NsIcon.tsx +9 -6
- package/src/common/icon/icons/UserIcon.tsx +8 -1
- package/src/common/icon/types.d.ts +3 -2
- package/src/common/progress/NsProgress.spec.tsx +2 -2
- package/src/common/progress/NsProgress.stories.tsx +10 -5
- package/src/common/progress/NsProgress.styled.tsx +30 -12
- package/src/common/progress/NsProgress.tsx +18 -16
- package/src/common/progress/types.d.ts +2 -2
- package/src/common/radiogroup/NsRadioGroup.spec.tsx +2 -2
- package/src/common/radiogroup/NsRadioGroup.stories.tsx +2 -2
- package/src/common/radiogroup/NsRadioGroup.tsx +5 -3
- package/src/common/radiogroup/types.d.ts +11 -12
- package/src/common/select/NsSelect.styled.tsx +37 -69
- package/src/common/select/NsSelect.tsx +86 -91
- package/src/common/select/NsSelectOption.tsx +10 -14
- package/src/common/select/specs/NsSelect.spec.tsx +183 -0
- package/src/common/select/specs/NsSelectOption.spec.tsx +32 -0
- package/src/common/select/stories/NsSelect.stories.tsx +25 -80
- package/src/common/select/stories/countries.ts +53 -0
- package/src/common/select/types.d.ts +21 -8
- package/src/common/table/NsTable.stories.tsx +1 -0
- package/src/common/cart-icon/NsCartIcon.styled.ts +0 -14
- package/src/common/select/NsSelect.spec.tsx +0 -274
package/package.json
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nuskin/foundation-ui-components",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.1",
|
|
4
4
|
"description": "It is a foundation library written in react, that provides common components to all UI pages",
|
|
5
|
-
"private": false,
|
|
6
5
|
"main": "src/common/index.ts",
|
|
7
6
|
"repository": {
|
|
8
7
|
"type": "git",
|
|
@@ -47,7 +46,7 @@
|
|
|
47
46
|
"@mui/material": "5.4.2",
|
|
48
47
|
"@mui/utils": "^5.14.1",
|
|
49
48
|
"@next/font": "13.2.4",
|
|
50
|
-
"@nuskin/foundation-theme": "1.0.0
|
|
49
|
+
"@nuskin/foundation-theme": "^1.0.0",
|
|
51
50
|
"framer-motion": "^10.8.5",
|
|
52
51
|
"next": "13.2.4",
|
|
53
52
|
"react": "18.2.0",
|
|
@@ -103,5 +102,6 @@
|
|
|
103
102
|
"files": [
|
|
104
103
|
"src/common"
|
|
105
104
|
],
|
|
106
|
-
"readme": "./README.md"
|
|
105
|
+
"readme": "./README.md",
|
|
106
|
+
"packageManager": "yarn@3.6.3"
|
|
107
107
|
}
|
|
@@ -25,26 +25,26 @@ const NsAuthDropdown = ({
|
|
|
25
25
|
darkModeEnabled = true,
|
|
26
26
|
}: NsAuthDropdownProps) => {
|
|
27
27
|
const [isOpen, setIsOpen] = useState(false);
|
|
28
|
-
const
|
|
28
|
+
const dropdownRef = useRef<HTMLDivElement | null>(null);
|
|
29
29
|
|
|
30
30
|
const handleToggle = useCallback(() => {
|
|
31
31
|
setIsOpen(!isOpen);
|
|
32
32
|
}, [isOpen]);
|
|
33
33
|
|
|
34
|
-
const handleClose = () => {
|
|
34
|
+
const handleClose = useCallback(() => {
|
|
35
35
|
setIsOpen(false);
|
|
36
|
-
};
|
|
36
|
+
},[]);
|
|
37
37
|
|
|
38
|
-
useOnClickOutside(
|
|
38
|
+
useOnClickOutside(dropdownRef?.current, handleClose);
|
|
39
39
|
|
|
40
40
|
const handleOnMouseLeave = useCallback(() => {
|
|
41
41
|
if (closeOnMouseLeave) {
|
|
42
42
|
handleClose();
|
|
43
43
|
}
|
|
44
|
-
}, [closeOnMouseLeave]);
|
|
44
|
+
}, [closeOnMouseLeave, handleClose]);
|
|
45
45
|
|
|
46
46
|
return (
|
|
47
|
-
<AuthDropdownContainer ref={
|
|
47
|
+
<AuthDropdownContainer ref={dropdownRef}>
|
|
48
48
|
<AuthHead data-testid="auth-head" onClick={handleToggle} darkModeEnabled={darkModeEnabled}>
|
|
49
49
|
<Button>{titleText}</Button>
|
|
50
50
|
<NsIcon testId="chevron-icon" name={isOpen ? 'arrowUp' : 'arrowDown'} color="primary" />
|
|
@@ -6,7 +6,6 @@ import { withCenterDecorator } from '@/stories/decorators/withCenterDecorator';
|
|
|
6
6
|
import { within } from '@storybook/testing-library';
|
|
7
7
|
import { expect } from '@storybook/jest';
|
|
8
8
|
import { nuskinTheme } from '@nuskin/foundation-theme';
|
|
9
|
-
|
|
10
9
|
const { palette } = nuskinTheme;
|
|
11
10
|
|
|
12
11
|
type ComponentType = typeof NsButton;
|
|
@@ -40,7 +39,9 @@ export default {
|
|
|
40
39
|
},
|
|
41
40
|
} as MetaStory;
|
|
42
41
|
|
|
43
|
-
export const button: Story = {
|
|
42
|
+
export const button: Story = {
|
|
43
|
+
args: {},
|
|
44
|
+
};
|
|
44
45
|
|
|
45
46
|
export const IconOnlyButton: Story = {
|
|
46
47
|
render: (args: any) => (
|
|
@@ -4,13 +4,38 @@ import { useTheme } from '@mui/material/styles';
|
|
|
4
4
|
import StyledButton from './NsButton.styled';
|
|
5
5
|
|
|
6
6
|
import { NsButtonProps } from './types.d';
|
|
7
|
+
import { NsIcon } from '../icon';
|
|
7
8
|
|
|
8
9
|
const NsButton = ({ children, darkModeEnabled = true, ...props }: PropsWithChildren<NsButtonProps>) => {
|
|
9
|
-
const { color } = props;
|
|
10
|
+
const { color, startIconName, endIconName, variant } = props;
|
|
10
11
|
const { palette } = useTheme();
|
|
11
12
|
const btnColor = color && palette?.[color] ? color : 'primary';
|
|
13
|
+
let iconColor = btnColor;
|
|
14
|
+
if (variant === 'contained') {
|
|
15
|
+
if (btnColor === 'secondary') {
|
|
16
|
+
iconColor = 'primary';
|
|
17
|
+
} else {
|
|
18
|
+
iconColor = 'secondary';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
let startIcon = props.startIcon;
|
|
22
|
+
if (!startIcon && startIconName) {
|
|
23
|
+
startIcon = <NsIcon name={startIconName} color={iconColor} size={props.size} />;
|
|
24
|
+
}
|
|
25
|
+
let endIcon = props.endIcon;
|
|
26
|
+
if (!endIcon && endIconName) {
|
|
27
|
+
endIcon = <NsIcon name={endIconName} color={iconColor} size={props.size} />;
|
|
28
|
+
}
|
|
29
|
+
|
|
12
30
|
return (
|
|
13
|
-
<StyledButton
|
|
31
|
+
<StyledButton
|
|
32
|
+
{...props}
|
|
33
|
+
startIcon={startIcon}
|
|
34
|
+
endIcon={endIcon}
|
|
35
|
+
color={btnColor}
|
|
36
|
+
disableRipple
|
|
37
|
+
darkModeEnabled={darkModeEnabled}
|
|
38
|
+
>
|
|
14
39
|
{children}
|
|
15
40
|
</StyledButton>
|
|
16
41
|
);
|
|
@@ -1,22 +1,26 @@
|
|
|
1
1
|
import { ButtonProps as MUIButtonProps } from '@mui/material';
|
|
2
|
+
import { NsIconProps } from '../icon';
|
|
2
3
|
|
|
3
4
|
export type NsButtonProps = Pick<
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
5
|
+
MUIButtonProps,
|
|
6
|
+
| 'aria-label'
|
|
7
|
+
| 'className'
|
|
8
|
+
| 'color'
|
|
9
|
+
| 'disabled'
|
|
10
|
+
| 'endIcon'
|
|
11
|
+
| 'fullWidth'
|
|
12
|
+
| 'href'
|
|
13
|
+
| 'onBlur'
|
|
14
|
+
| 'onClick'
|
|
15
|
+
| 'onChange'
|
|
16
|
+
| 'onFocus'
|
|
17
|
+
| 'size'
|
|
18
|
+
| 'startIcon'
|
|
19
|
+
| 'type'
|
|
20
|
+
| 'variant'
|
|
19
21
|
> & {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
iconOnly?: boolean;
|
|
23
|
+
darkModeEnabled?: boolean;
|
|
24
|
+
startIconName?: NsIconProps['name'];
|
|
25
|
+
endIconName?: NsIconProps['name'];
|
|
22
26
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { NsLink, NsImage } from '../';
|
|
2
2
|
import { NsFfgStyled } from './styled/NsFfgStyled';
|
|
3
3
|
import { NsFfgCardProps } from './types.d';
|
|
4
4
|
|
|
@@ -10,7 +10,7 @@ const NsFfgCard = ({ cardLink, text, topImageUrl, topImageAlt, additionalProps,
|
|
|
10
10
|
className={additionalProps?.className ?? ''}
|
|
11
11
|
>
|
|
12
12
|
<header className="top-image">
|
|
13
|
-
<
|
|
13
|
+
<NsImage height={192} width={288} alt={topImageAlt} src={topImageUrl} />
|
|
14
14
|
</header>
|
|
15
15
|
<article className="body-text" {...cslp?.card_text}>
|
|
16
16
|
{text}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Badge, Theme } from '@mui/material';
|
|
2
|
+
import { ColorUtils, styled, FoundationTheme } from '@nuskin/foundation-theme';
|
|
3
|
+
|
|
4
|
+
type ItemCountParams = {
|
|
5
|
+
theme?: Theme;
|
|
6
|
+
bgcolor: FoundationTheme.PaletteColors;
|
|
7
|
+
};
|
|
8
|
+
const { paletteColor } = ColorUtils;
|
|
9
|
+
export const StyledBadge = styled(Badge)<ItemCountParams>`
|
|
10
|
+
& .MuiBadge-badge {
|
|
11
|
+
background-color: ${({ bgcolor, theme }) => paletteColor(bgcolor)({ theme })};
|
|
12
|
+
color: ${({ bgcolor, theme }) => paletteColor(bgcolor, 'contrastText')({ theme })};
|
|
13
|
+
}
|
|
14
|
+
`;
|
|
@@ -19,24 +19,24 @@ const NsDropdown: React.FC<DropdownProps> = ({
|
|
|
19
19
|
}) => {
|
|
20
20
|
const [isOpen, setIsOpen] = useState(false);
|
|
21
21
|
const [value, setValue] = useState(selectedValue || '');
|
|
22
|
-
const
|
|
22
|
+
const dropdownRef = useRef<HTMLDivElement | null>(null);
|
|
23
23
|
|
|
24
24
|
const handleToggle = useCallback(() => {
|
|
25
25
|
if (disabled) return;
|
|
26
26
|
setIsOpen((x) => !x);
|
|
27
27
|
}, [disabled]);
|
|
28
28
|
|
|
29
|
-
const handleClose = () => {
|
|
29
|
+
const handleClose = useCallback(() => {
|
|
30
30
|
setIsOpen(false);
|
|
31
|
-
};
|
|
31
|
+
}, []);
|
|
32
32
|
|
|
33
|
-
useOnClickOutside(
|
|
33
|
+
useOnClickOutside(dropdownRef?.current, handleClose);
|
|
34
34
|
|
|
35
35
|
const handleOnMouseLeave = useCallback(() => {
|
|
36
36
|
if (closeOnMouseLeave) {
|
|
37
37
|
handleClose();
|
|
38
38
|
}
|
|
39
|
-
}, [closeOnMouseLeave]);
|
|
39
|
+
}, [closeOnMouseLeave, handleClose]);
|
|
40
40
|
|
|
41
41
|
const handleSelect = (input: string) => {
|
|
42
42
|
onSelect(input);
|
|
@@ -45,7 +45,7 @@ const NsDropdown: React.FC<DropdownProps> = ({
|
|
|
45
45
|
};
|
|
46
46
|
|
|
47
47
|
return (
|
|
48
|
-
<DropdownContainer disabled={disabled} ref={
|
|
48
|
+
<DropdownContainer disabled={disabled} ref={dropdownRef}>
|
|
49
49
|
<DropdownHead data-testid="dropdown-head" disabled={disabled} type={type} onClick={handleToggle}>
|
|
50
50
|
<DropdownButton>{selectedLabel || placeholder}</DropdownButton>
|
|
51
51
|
<NsIcon testId="chevron-icon" name={isOpen ? 'arrowUp' : 'arrowDown'} color="primary" />
|
|
@@ -43,7 +43,7 @@ const NsEmailSimple = (props: NsEmailSimpleProps) => {
|
|
|
43
43
|
errorMessageValue = '';
|
|
44
44
|
}
|
|
45
45
|
} catch (error) {
|
|
46
|
-
console.
|
|
46
|
+
console.error('API error', error);
|
|
47
47
|
}
|
|
48
48
|
} else {
|
|
49
49
|
//TODO What happens when user does not pass valid checkEmail function
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import { render } from '@/test-utils/index';
|
|
1
2
|
import React from 'react';
|
|
2
3
|
|
|
3
|
-
import { render } from '@/test-utils/index';
|
|
4
4
|
import { NsFlag } from '../';
|
|
5
5
|
|
|
6
6
|
describe('FlagComponent', () => {
|
|
7
|
-
it('renders a div element with background color and text', () => {
|
|
7
|
+
it('1. renders a div element with background color and text', () => {
|
|
8
8
|
const text = 'NU SKIN';
|
|
9
9
|
const { getByText } = render(<NsFlag message="NU SKIN" color="accentColorA" />);
|
|
10
10
|
const divElement = getByText(text);
|
|
@@ -12,4 +12,16 @@ describe('FlagComponent', () => {
|
|
|
12
12
|
expect(divElement).toBeInTheDocument();
|
|
13
13
|
expect(divElement).toHaveTextContent(text);
|
|
14
14
|
});
|
|
15
|
+
it('2. should render a flag with default size, color, and testId when only message prop is provided', () => {
|
|
16
|
+
const props = {
|
|
17
|
+
message: 'Test Message',
|
|
18
|
+
};
|
|
19
|
+
const { getByTestId } = render(<NsFlag {...props} />);
|
|
20
|
+
const flag = getByTestId('ns-flag-component');
|
|
21
|
+
expect(flag).toBeInTheDocument();
|
|
22
|
+
expect(flag).toHaveStyle('height: 20px');
|
|
23
|
+
expect(flag).toHaveStyle('color: #ffffff');
|
|
24
|
+
expect(flag).toHaveStyle('background: #f56767');
|
|
25
|
+
expect(flag).toHaveTextContent(props.message);
|
|
26
|
+
});
|
|
15
27
|
});
|
|
@@ -27,7 +27,7 @@ const FlagHeightMap = {
|
|
|
27
27
|
};
|
|
28
28
|
|
|
29
29
|
const getFlagHeight = ({ $size }: { $size: NsFlagSize }) => {
|
|
30
|
-
return FlagHeightMap[$size]
|
|
30
|
+
return FlagHeightMap[$size];
|
|
31
31
|
};
|
|
32
32
|
|
|
33
33
|
const makeFlagSize = ({ $size }: { $size: NsFlagSize }) => {
|
|
@@ -36,10 +36,7 @@ const makeFlagSize = ({ $size }: { $size: NsFlagSize }) => {
|
|
|
36
36
|
};
|
|
37
37
|
|
|
38
38
|
const makeFlagBorder = ({ $size, $bgColor = 'accentColorA', theme }: NsFlagThemeParams) => {
|
|
39
|
-
|
|
40
|
-
return `${makeFlagSize({ $size })}px solid ${paletteColor($bgColor)({ theme })}`;
|
|
41
|
-
}
|
|
42
|
-
return '';
|
|
39
|
+
return `${makeFlagSize({ $size })}px solid ${paletteColor($bgColor)({ theme })}`;
|
|
43
40
|
};
|
|
44
41
|
|
|
45
42
|
export const StyledFlag = styled('span')<NsFlagThemeParams>`
|
|
@@ -1,28 +1,25 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
2
|
|
|
3
3
|
export type Handler = () => void;
|
|
4
4
|
|
|
5
|
-
export function useOnClickOutside
|
|
6
|
-
|
|
5
|
+
export function useOnClickOutside(
|
|
6
|
+
element: HTMLDivElement | null,
|
|
7
7
|
handler: Handler = () => {},
|
|
8
8
|
mouseEvent: 'click' | 'mousedown' | 'mouseup' = 'click'
|
|
9
9
|
): void {
|
|
10
|
-
|
|
11
|
-
(event: MouseEvent) => {
|
|
12
|
-
|
|
13
|
-
if (!
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
const eventListener = (event: MouseEvent) => {
|
|
12
|
+
// Do nothing if clicking ref's element or descendent elements
|
|
13
|
+
if (!element || element.contains(event.target as Node)) {
|
|
14
14
|
return;
|
|
15
15
|
}
|
|
16
16
|
handler?.();
|
|
17
|
-
}
|
|
18
|
-
[ref, handler]
|
|
19
|
-
);
|
|
20
|
-
useEffect(() => {
|
|
17
|
+
};
|
|
21
18
|
if (document) {
|
|
22
|
-
document.addEventListener(mouseEvent,
|
|
19
|
+
document.addEventListener(mouseEvent, eventListener);
|
|
23
20
|
return () => {
|
|
24
|
-
document.removeEventListener(mouseEvent,
|
|
21
|
+
document.removeEventListener(mouseEvent, eventListener);
|
|
25
22
|
};
|
|
26
23
|
}
|
|
27
|
-
}, [mouseEvent,
|
|
24
|
+
}, [mouseEvent, handler, element]);
|
|
28
25
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { render, screen } from '@/test-utils/index';
|
|
2
|
+
import { fireEvent } from '@storybook/testing-library';
|
|
2
3
|
import React from 'react';
|
|
3
4
|
|
|
4
5
|
import { NsIcon, NsIconNameType, NsIconVariants } from './';
|
|
5
6
|
|
|
6
7
|
describe('NsIcon Component', () => {
|
|
7
8
|
it('1. renders cartBadge icon with custom size and color', () => {
|
|
8
|
-
const { getByTestId } = render(<NsIcon name="cart" size=
|
|
9
|
+
const { getByTestId } = render(<NsIcon name="cart" size={'medium'} color="secondary" />);
|
|
9
10
|
const icon = getByTestId('icon');
|
|
10
11
|
const svgIcon = icon.querySelector('svg');
|
|
11
12
|
// expect(svgIcon).toHaveClass('MuiSvgIcon-colorSecondary');
|
|
@@ -35,8 +36,16 @@ describe('NsIcon Component', () => {
|
|
|
35
36
|
consoleErrorSpy.mockRestore();
|
|
36
37
|
});
|
|
37
38
|
it('5. renders default color of alertErrorIcon', () => {
|
|
38
|
-
const { getByTestId } = render(<NsIcon name="alertError" size=
|
|
39
|
+
const { getByTestId } = render(<NsIcon name="alertError" size={'medium'} />);
|
|
39
40
|
const icon = getByTestId('alertError');
|
|
40
41
|
expect(icon).toBeInTheDocument();
|
|
41
42
|
});
|
|
43
|
+
it('6. calls onClick event handler', () => {
|
|
44
|
+
const onClick = jest.fn();
|
|
45
|
+
const { getByTestId } = render(<NsIcon name="alertError" size="medium" onClick={onClick} />);
|
|
46
|
+
const icon = getByTestId('alertError');
|
|
47
|
+
expect(icon).toBeInTheDocument();
|
|
48
|
+
fireEvent.click(icon);
|
|
49
|
+
expect(onClick).toHaveBeenCalled();
|
|
50
|
+
});
|
|
42
51
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { PaletteColors } from '@nuskin/foundation-theme';
|
|
1
2
|
import type { StoryObj, Meta } from '@storybook/react';
|
|
2
3
|
import { withCenterDecorator } from '@/stories/decorators/withCenterDecorator';
|
|
3
4
|
import { NsIcon, NsIconVariants } from './';
|
|
@@ -18,7 +19,7 @@ export default {
|
|
|
18
19
|
},
|
|
19
20
|
color: {
|
|
20
21
|
control: 'select',
|
|
21
|
-
options:
|
|
22
|
+
options: PaletteColors,
|
|
22
23
|
},
|
|
23
24
|
colorOverride: {
|
|
24
25
|
control: 'color',
|
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
import { styled } from '@nuskin/foundation-theme';
|
|
2
2
|
import { NsIconSizeType } from './types.d';
|
|
3
3
|
|
|
4
|
-
const iconSize = {
|
|
4
|
+
export const iconSize: Record<NsIconSizeType, string> = {
|
|
5
5
|
small: '16px',
|
|
6
6
|
medium: '20px',
|
|
7
7
|
large: '24px',
|
|
8
8
|
xLarge: '30px',
|
|
9
9
|
};
|
|
10
|
+
type IconWrapperType = { size: NsIconSizeType };
|
|
10
11
|
|
|
11
|
-
export const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
12
|
+
export const IconWrapper = styled('div')<IconWrapperType>(({ size }) => {
|
|
13
|
+
return {
|
|
14
|
+
display: 'flex',
|
|
15
|
+
placeContent: 'center',
|
|
16
|
+
placeItems: 'center',
|
|
17
|
+
svg: {
|
|
18
|
+
width: iconSize[size],
|
|
19
|
+
height: iconSize[size],
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
});
|
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
import { useTheme } from '@mui/material';
|
|
2
2
|
import { ColorUtils } from '@nuskin/foundation-theme';
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
import { IconWrapper } from './NsIcon.styled';
|
|
4
5
|
import IconVariants from './NsIconVariants';
|
|
5
|
-
import { NsIconProps } from './types.d';
|
|
6
|
+
import { NsIconNameType, NsIconProps } from './types.d';
|
|
6
7
|
|
|
7
8
|
const { paletteColor } = ColorUtils;
|
|
9
|
+
|
|
8
10
|
const NsIcon = ({
|
|
9
11
|
name,
|
|
10
|
-
size
|
|
12
|
+
size,
|
|
11
13
|
color = 'primary',
|
|
12
14
|
testId = 'icon',
|
|
13
15
|
className,
|
|
14
16
|
colorOverride,
|
|
17
|
+
onClick = () => {},
|
|
15
18
|
}: NsIconProps) => {
|
|
16
|
-
const Icon = IconVariants[name];
|
|
19
|
+
const Icon = IconVariants[name as NsIconNameType];
|
|
17
20
|
const theme = useTheme();
|
|
18
21
|
const iconColor = paletteColor(color)({ theme });
|
|
19
22
|
if (!Icon) {
|
|
@@ -21,9 +24,9 @@ const NsIcon = ({
|
|
|
21
24
|
}
|
|
22
25
|
|
|
23
26
|
return (
|
|
24
|
-
<
|
|
27
|
+
<IconWrapper data-testid={testId} size={size || 'large'} className={className} onClick={onClick}>
|
|
25
28
|
<Icon color={colorOverride ? colorOverride : (iconColor as any)} />
|
|
26
|
-
</
|
|
29
|
+
</IconWrapper>
|
|
27
30
|
);
|
|
28
31
|
};
|
|
29
32
|
|
|
@@ -6,7 +6,13 @@ import SvgIcon, { SvgIconProps } from '@mui/material/SvgIcon';
|
|
|
6
6
|
*/
|
|
7
7
|
const UserIcon = (props: SvgIconProps) => {
|
|
8
8
|
return (
|
|
9
|
-
<SvgIcon
|
|
9
|
+
<SvgIcon
|
|
10
|
+
viewBox="0 0 24 24"
|
|
11
|
+
data-testid="userIcon"
|
|
12
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
13
|
+
{...props}
|
|
14
|
+
fill="transparent"
|
|
15
|
+
>
|
|
10
16
|
<path
|
|
11
17
|
fillRule="evenodd"
|
|
12
18
|
clipRule="evenodd"
|
|
@@ -17,6 +23,7 @@ const UserIcon = (props: SvgIconProps) => {
|
|
|
17
23
|
<path
|
|
18
24
|
d="M19 20C18.9425 19.3229 19 17.5669 17.3793 17.2226C14.7126 16.6144 13.931 16.4078 14.1034 14.2272C15.8046 13.0796 15.7816 11.0252 15.7471 8.74139C15.7471 6.67508 14.0695 5 12 5C9.93052 5 8.25287 6.67508 8.25287 8.74139C8.25287 11.0367 8.25287 13.137 9.89655 14.2272C10.0575 16.4078 9.28736 16.6144 6.62069 17.2226C5 17.5669 5.05747 19.3229 5 20"
|
|
19
25
|
stroke={props.color}
|
|
26
|
+
fill={props.color}
|
|
20
27
|
strokeWidth="2"
|
|
21
28
|
strokeLinecap="round"
|
|
22
29
|
/>
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { FoundationTheme } from '@nuskin/foundation-theme';
|
|
2
|
+
import { HTMLProps } from 'react';
|
|
3
|
+
|
|
2
4
|
import { NsIconVariants } from '.';
|
|
3
5
|
|
|
4
6
|
export type NsIconSizeType = 'small' | 'medium' | 'large' | 'xLarge';
|
|
5
7
|
|
|
6
8
|
export type NsIconNameType = keyof typeof NsIconVariants;
|
|
7
9
|
|
|
8
|
-
export type NsIconProps = {
|
|
10
|
+
export type NsIconProps = HTMLProps & {
|
|
9
11
|
name: NsIconNameType;
|
|
10
12
|
size?: NsIconSizeType;
|
|
11
13
|
color?: FoundationTheme.PaletteColors;
|
|
12
14
|
testId?: string;
|
|
13
|
-
className?: string;
|
|
14
15
|
colorOverride?: string;
|
|
15
16
|
};
|
|
@@ -4,7 +4,7 @@ import Progress from './NsProgress';
|
|
|
4
4
|
|
|
5
5
|
describe(' NsProgress Component', () => {
|
|
6
6
|
test('should render Progress bar', () => {
|
|
7
|
-
render(<Progress
|
|
7
|
+
render(<Progress barColor="primary" value={10}></Progress>);
|
|
8
8
|
|
|
9
9
|
const pbar = screen.getByRole('progressbar');
|
|
10
10
|
|
|
@@ -16,7 +16,7 @@ describe(' NsProgress Component', () => {
|
|
|
16
16
|
const value = 50;
|
|
17
17
|
|
|
18
18
|
// Render the StyledProgress component with the value prop
|
|
19
|
-
const { container } = render(<Progress
|
|
19
|
+
const { container } = render(<Progress barColor="primary" value={value} />);
|
|
20
20
|
|
|
21
21
|
// Get the progress bar element
|
|
22
22
|
const progressBar = container.querySelector('.MuiLinearProgress-bar');
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { withCenterDecorator } from '@/stories/decorators/withCenterDecorator';
|
|
2
2
|
import type { StoryObj, Meta } from '@storybook/react';
|
|
3
3
|
import NsProgress from './NsProgress';
|
|
4
|
+
import { PaletteColors } from '@nuskin/foundation-theme';
|
|
4
5
|
|
|
5
6
|
type ComponentType = typeof NsProgress;
|
|
6
7
|
type Story = StoryObj<ComponentType>;
|
|
@@ -8,10 +9,12 @@ type MetaStory = Meta<ComponentType>;
|
|
|
8
9
|
|
|
9
10
|
export default {
|
|
10
11
|
component: NsProgress,
|
|
11
|
-
title: '
|
|
12
|
+
title: 'Components/Inputs/NsProgress',
|
|
12
13
|
decorators: [withCenterDecorator],
|
|
13
14
|
argTypes: {
|
|
14
|
-
|
|
15
|
+
barColor: {
|
|
16
|
+
control: 'select',
|
|
17
|
+
options: PaletteColors,
|
|
15
18
|
description: 'defines the color for the completed progress',
|
|
16
19
|
},
|
|
17
20
|
height: {
|
|
@@ -21,6 +24,8 @@ export default {
|
|
|
21
24
|
description: 'value of the progress done',
|
|
22
25
|
},
|
|
23
26
|
backgroundColor: {
|
|
27
|
+
control: 'select',
|
|
28
|
+
options: PaletteColors,
|
|
24
29
|
description: 'color of incomplete progress',
|
|
25
30
|
},
|
|
26
31
|
},
|
|
@@ -28,9 +33,9 @@ export default {
|
|
|
28
33
|
|
|
29
34
|
export const ProgressBar: Story = {
|
|
30
35
|
args: {
|
|
31
|
-
|
|
32
|
-
backgroundColor: '
|
|
33
|
-
value:
|
|
36
|
+
barColor: 'primary',
|
|
37
|
+
backgroundColor: 'secondary',
|
|
38
|
+
value: 40,
|
|
34
39
|
height: '10px',
|
|
35
40
|
body: <span>{'% Complete'}</span>,
|
|
36
41
|
},
|
|
@@ -2,8 +2,9 @@ import { styled, ColorUtils } from '@nuskin/foundation-theme';
|
|
|
2
2
|
import { LinearProgress } from '@mui/material';
|
|
3
3
|
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
|
|
4
4
|
import { NsProgressProps } from './types';
|
|
5
|
+
import { NsTypography } from '../typography';
|
|
5
6
|
|
|
6
|
-
const { color } = ColorUtils;
|
|
7
|
+
const { color, paletteColor } = ColorUtils;
|
|
7
8
|
export const StyledCheckCircle = styled(CheckCircleIcon)`
|
|
8
9
|
color: ${color('neutral', '50')};
|
|
9
10
|
padding-top: 0.6em;
|
|
@@ -11,29 +12,46 @@ export const StyledCheckCircle = styled(CheckCircleIcon)`
|
|
|
11
12
|
justify-self: flex-end;
|
|
12
13
|
`;
|
|
13
14
|
|
|
14
|
-
export const
|
|
15
|
-
color: ${color('primary', '100')};
|
|
15
|
+
export const ProgressInfo = styled(NsTypography)`
|
|
16
16
|
display: flex;
|
|
17
17
|
justify-content: flex-end;
|
|
18
|
-
font-family: Inter;
|
|
19
|
-
font-size: 12px;
|
|
20
|
-
font-style: normal;
|
|
21
18
|
font-weight: 700;
|
|
22
19
|
text-transform: uppercase;
|
|
23
20
|
`;
|
|
24
21
|
|
|
25
22
|
export const StyledLinearProgress = styled(LinearProgress)<Omit<NsProgressProps, 'value'>>(
|
|
26
|
-
({ height, backgroundColor,
|
|
23
|
+
({ height, backgroundColor, barColor, theme }) => ({
|
|
27
24
|
width: '334px',
|
|
28
25
|
height,
|
|
29
|
-
backgroundColor,
|
|
30
|
-
borderRadius: '
|
|
26
|
+
backgroundColor: paletteColor(backgroundColor)({ theme }),
|
|
27
|
+
borderRadius: '8px',
|
|
31
28
|
display: 'inline-block',
|
|
32
29
|
|
|
33
30
|
'.MuiLinearProgress-bar': {
|
|
34
|
-
backgroundColor:
|
|
31
|
+
backgroundColor: paletteColor(barColor)({ theme }),
|
|
35
32
|
},
|
|
36
33
|
})
|
|
37
34
|
);
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
export const StyledProgress = styled('div')(() => {
|
|
36
|
+
return {
|
|
37
|
+
display: 'grid',
|
|
38
|
+
gridTemplateRows: '30px 30px',
|
|
39
|
+
'.progress-info': {
|
|
40
|
+
justifyContent: 'flex-end',
|
|
41
|
+
display: 'flex',
|
|
42
|
+
alignItems: 'center',
|
|
43
|
+
},
|
|
44
|
+
'.progress-bar': {
|
|
45
|
+
display: 'flex',
|
|
46
|
+
alignItems: 'center',
|
|
47
|
+
'.MuiSvgIcon-root': {
|
|
48
|
+
padding: '0',
|
|
49
|
+
margin: '0',
|
|
50
|
+
height: '100%',
|
|
51
|
+
position: 'relative',
|
|
52
|
+
left: '-4px',
|
|
53
|
+
scale: '1.3',
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
});
|