@nuskin/foundation-ui-components 1.0.0-develop.3 → 1.0.0-develop.5
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 +9 -4
- package/src/common/accordion/NsAccordion.tsx +77 -0
- package/src/common/accordion/NsAccordionHeader.tsx +56 -0
- package/src/common/accordion/NsAccordionItem.tsx +39 -0
- package/src/common/accordion/NsCollapse.tsx +56 -0
- package/src/common/accordion/NsToggleSection.tsx +30 -0
- package/src/common/accordion/ToggleSection.styled.tsx +28 -0
- package/src/common/accordion/index.ts +6 -0
- package/src/common/accordion/specs/NsAccordion.spec.tsx +93 -0
- package/src/common/accordion/specs/NsAccordionHeader.spec.tsx +63 -0
- package/src/common/accordion/specs/NsAccordionItem.spec.tsx +62 -0
- package/src/common/accordion/specs/NsCollapse.spec.tsx +37 -0
- package/src/common/accordion/specs/NsToggleSection.spec.tsx +38 -0
- package/src/common/accordion/stories/NsAccordion.stories.tsx +47 -0
- package/src/common/accordion/stories/NsAccordionHeader.stories.tsx +40 -0
- package/src/common/accordion/stories/NsAccordionItem.stories.tsx +40 -0
- package/src/common/accordion/stories/NsCollapse.stories.tsx +35 -0
- package/src/common/accordion/stories/NsToggleSection.stories.tsx +23 -0
- package/src/common/accordion/stories/data.tsx +22 -0
- package/src/common/accordion/types.d.ts +73 -0
- package/src/common/alert/NsAlert.spec.tsx +0 -1
- package/src/common/alert/NsAlert.styled.ts +10 -10
- package/src/common/alert/NsAlert.tsx +4 -3
- package/src/common/alert/types.d.ts +22 -22
- package/src/common/auth-dropdown/NsAuthDropdown.styled.ts +12 -11
- package/src/common/auth-dropdown/NsAuthDropdown.tsx +2 -1
- package/src/common/auth-dropdown/types.d.ts +3 -1
- package/src/common/autocomplete/NsAutocomplete.spec.tsx +20 -8
- package/src/common/autocomplete/NsAutocomplete.stories.tsx +3 -0
- package/src/common/autocomplete/NsAutocomplete.styled.tsx +12 -0
- package/src/common/autocomplete/NsAutocomplete.tsx +36 -18
- package/src/common/autocomplete/types.d.ts +7 -1
- package/src/common/brand-components/NsBrandComponent.tsx +3 -0
- package/src/common/breadcrumb/NsBreadcrumb.tsx +4 -2
- package/src/common/button/NsButton.spec.tsx +11 -12
- package/src/common/button/NsButton.stories.tsx +1 -1
- package/src/common/button/NsButton.styled.tsx +25 -23
- package/src/common/button/NsButton.tsx +9 -2
- package/src/common/button/types.d.ts +1 -0
- package/src/common/checkbox/NsCheckbox.styled.tsx +2 -2
- package/src/common/common.types.d.ts +2 -0
- package/src/common/dropdown/types.d.ts +3 -1
- package/src/common/email-simple/NsEmailSimple.spec.tsx +145 -0
- package/src/common/email-simple/NsEmailSimple.stories.tsx +61 -0
- package/src/common/email-simple/NsEmailSimple.tsx +71 -0
- package/src/common/email-simple/index.ts +2 -0
- package/src/common/email-simple/types.d.ts +14 -0
- package/src/common/flag/NsFlag.spec.tsx +4 -24
- package/src/common/flag/NsFlag.styled.tsx +5 -5
- package/src/common/flag/NsFlag.tsx +0 -2
- package/src/common/icon/types.d.ts +1 -0
- package/src/common/image/types.d.ts +1 -0
- package/src/common/index.ts +3 -0
- package/src/common/input/InputFieldWrapper.tsx +4 -4
- package/src/common/input/NsInputField.stories.tsx +11 -9
- package/src/common/input/NsInputField.tsx +12 -5
- package/src/common/input/getAdornmentProps.tsx +4 -0
- package/src/common/input/types.d.ts +3 -1
- package/src/common/label/NsLabel.stories.tsx +3 -0
- package/src/common/label/NsLabel.tsx +18 -17
- package/src/common/label/types.d.ts +2 -1
- package/src/common/nav-tab/NsNavTab.spec.tsx +0 -5
- package/src/common/nav-tab/NsNavTab.styled.tsx +7 -4
- package/src/common/radiogroup/NsRadioGroup.spec.tsx +64 -0
- package/src/common/radiogroup/NsRadioGroup.stories.tsx +102 -0
- package/src/common/radiogroup/NsRadioGroup.styled.tsx +50 -0
- package/src/common/radiogroup/NsRadioGroup.tsx +61 -0
- package/src/common/radiogroup/index.ts +2 -0
- package/src/common/radiogroup/types.d.ts +20 -0
- package/src/common/select/NsSelect.spec.tsx +200 -0
- package/src/common/select/NsSelect.styled.tsx +85 -0
- package/src/common/select/NsSelect.tsx +117 -0
- package/src/common/select/NsSelectOption.tsx +30 -0
- package/src/common/select/index.ts +3 -0
- package/src/common/select/stories/NsSelect.stories.tsx +134 -0
- package/src/common/select/types.d.ts +34 -0
- package/src/common/table/NsExpandableRow.tsx +68 -0
- package/src/common/table/NsTable.spec.tsx +69 -41
- package/src/common/table/NsTable.stories.tsx +45 -7
- package/src/common/table/NsTable.styled.ts +106 -12
- package/src/common/table/NsTable.tsx +78 -89
- package/src/common/table/NsTableHeader.tsx +52 -38
- package/src/common/table/NsTablePagination.tsx +21 -13
- package/src/common/table/types.d.ts +21 -8
- package/src/common/text-strip/NsTextStrip.tsx +10 -7
- package/src/common/typography/Typography.styled.tsx +3 -3
- package/src/common/validations/regex.ts +3 -0
- /package/src/common/card/{tests → specs}/BrandLogo.spec.tsx +0 -0
- /package/src/common/card/{tests → specs}/NsBgCard.spec.tsx +0 -0
- /package/src/common/card/{tests → specs}/NsBrandProductCard.spec.tsx +0 -0
- /package/src/common/card/{tests → specs}/NsFfgCard.spec.tsx +0 -0
- /package/src/common/carousal/{tests → specs}/Chevron.spec.tsx +0 -0
- /package/src/common/carousal/{tests → specs}/NsCarousal.spec.tsx +0 -0
- /package/src/common/carousal/{tests → specs}/NsScrollingCarousal.spec.tsx +0 -0
- /package/src/common/carousal/{tests → specs}/RenderBrand.spec.tsx +0 -0
|
@@ -1,20 +1,23 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { NsInputField } from '../input';
|
|
1
|
+
import React, { useState, PropsWithChildren, useRef } from 'react';
|
|
3
2
|
import { Autocomplete, AutocompleteRenderGroupParams, List, AutocompleteRenderInputParams } from '@mui/material';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { NsButton } from '../button';
|
|
3
|
+
import { NsTypography, NsButton } from '../';
|
|
4
|
+
import { NsAutocompleteProps, OptionsProps, ComponentBoxProps } from './types.d';
|
|
5
|
+
import { StyledTextField } from './NsAutocomplete.styled';
|
|
8
6
|
|
|
9
7
|
const NsAutocomplete = ({
|
|
10
8
|
id = 'custom_autocomplete_dropdown',
|
|
11
9
|
options = [],
|
|
12
10
|
renderOptions,
|
|
11
|
+
loading,
|
|
12
|
+
noOptionsText,
|
|
13
13
|
onSelect,
|
|
14
|
+
onChange,
|
|
14
15
|
clear,
|
|
15
16
|
seeMore,
|
|
16
17
|
placeholder,
|
|
17
18
|
className,
|
|
19
|
+
iconStart,
|
|
20
|
+
iconEnd,
|
|
18
21
|
}: PropsWithChildren<NsAutocompleteProps>) => {
|
|
19
22
|
const inputRef = useRef(null);
|
|
20
23
|
|
|
@@ -27,10 +30,21 @@ const NsAutocomplete = ({
|
|
|
27
30
|
};
|
|
28
31
|
|
|
29
32
|
const renderInput = (params: AutocompleteRenderInputParams) => (
|
|
30
|
-
<
|
|
33
|
+
<StyledTextField
|
|
34
|
+
{...params}
|
|
35
|
+
onChange={onChange}
|
|
36
|
+
placeholder={placeholder}
|
|
37
|
+
inputRef={inputRef}
|
|
38
|
+
InputProps={{
|
|
39
|
+
...params.InputProps,
|
|
40
|
+
startAdornment: iconStart,
|
|
41
|
+
endAdornment: iconEnd,
|
|
42
|
+
}}
|
|
43
|
+
/>
|
|
31
44
|
);
|
|
32
45
|
|
|
33
|
-
|
|
46
|
+
// TODO: @vijayprathap need to check if we can remove object spread of ...props and ...params in renderInput
|
|
47
|
+
const renderOption = (props: ComponentBoxProps, option: OptionsProps) => (
|
|
34
48
|
<li {...props}>
|
|
35
49
|
<OptionUI {...props} option={option} />
|
|
36
50
|
</li>
|
|
@@ -43,7 +57,7 @@ const NsAutocomplete = ({
|
|
|
43
57
|
const renderGroupName = ({ key, group, children }: AutocompleteRenderGroupParams) => (
|
|
44
58
|
<li key={key}>
|
|
45
59
|
{group ? (
|
|
46
|
-
<NsTypography variant="p-
|
|
60
|
+
<NsTypography variant="p-m" weight="bold" className="p-3">
|
|
47
61
|
{group}
|
|
48
62
|
</NsTypography>
|
|
49
63
|
) : null}
|
|
@@ -59,7 +73,9 @@ const NsAutocomplete = ({
|
|
|
59
73
|
return (
|
|
60
74
|
<div className="text-right p-3">
|
|
61
75
|
<NsButton onClick={onClear} data-testid="autocomplete-clear-id">
|
|
62
|
-
<NsTypography variant={'p-xs'}>
|
|
76
|
+
<NsTypography variant={'p-xs'} weight="bold">
|
|
77
|
+
{text}
|
|
78
|
+
</NsTypography>
|
|
63
79
|
</NsButton>
|
|
64
80
|
</div>
|
|
65
81
|
);
|
|
@@ -73,20 +89,20 @@ const NsAutocomplete = ({
|
|
|
73
89
|
return (
|
|
74
90
|
<div className="text-center p-3">
|
|
75
91
|
<NsButton onClick={onSeeMore} data-testid="autocomplete-loadmore-id">
|
|
76
|
-
<NsTypography variant="p-xs">
|
|
92
|
+
<NsTypography variant="p-xs" weight="bold">
|
|
93
|
+
{text}
|
|
94
|
+
</NsTypography>
|
|
77
95
|
</NsButton>
|
|
78
96
|
</div>
|
|
79
97
|
);
|
|
80
98
|
};
|
|
81
99
|
|
|
82
|
-
const renderListBoxComponent = ({ children, ...otherProps }:
|
|
83
|
-
|
|
100
|
+
const renderListBoxComponent = ({ children, ...otherProps }: ComponentBoxProps) => (
|
|
101
|
+
<List {...otherProps}>
|
|
84
102
|
{renderClearButton()}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
</List>
|
|
89
|
-
</>
|
|
103
|
+
{children}
|
|
104
|
+
{renderSeeMoreButton()}
|
|
105
|
+
</List>
|
|
90
106
|
);
|
|
91
107
|
|
|
92
108
|
return (
|
|
@@ -95,6 +111,8 @@ const NsAutocomplete = ({
|
|
|
95
111
|
disablePortal
|
|
96
112
|
className={className}
|
|
97
113
|
options={options}
|
|
114
|
+
loading={loading}
|
|
115
|
+
noOptionsText={noOptionsText}
|
|
98
116
|
groupBy={renderGroupBy}
|
|
99
117
|
getOptionLabel={renderOptionLabel}
|
|
100
118
|
renderOption={renderOption}
|
|
@@ -6,9 +6,13 @@ import { ComponentsProps } from '@mui/material';
|
|
|
6
6
|
export type NsAutocompleteProps = {
|
|
7
7
|
id?: string;
|
|
8
8
|
options: OptionsProps[];
|
|
9
|
+
loading?:boolean;
|
|
9
10
|
renderOptions: React.ComponentType<any>;
|
|
11
|
+
noOptionsText ?:string;
|
|
10
12
|
// eslint-disable-next-line no-unused-vars
|
|
11
13
|
onSelect?: (params: OptionsProps | null) => void;
|
|
14
|
+
// eslint-disable-next-line no-unused-vars
|
|
15
|
+
onChange?: (params) => void;
|
|
12
16
|
clear?: {
|
|
13
17
|
text: string;
|
|
14
18
|
show?: boolean; //default to false
|
|
@@ -21,6 +25,8 @@ export type NsAutocompleteProps = {
|
|
|
21
25
|
};
|
|
22
26
|
placeholder?: string;
|
|
23
27
|
className?: string;
|
|
28
|
+
iconStart?: string | React.ReactNode;
|
|
29
|
+
iconEnd?: string | React.ReactNode;
|
|
24
30
|
} & AdditionalProps;
|
|
25
31
|
|
|
26
32
|
export type ListUiProps = {
|
|
@@ -36,4 +42,4 @@ export type OptionsProps = {
|
|
|
36
42
|
groupBgColor?: string;
|
|
37
43
|
};
|
|
38
44
|
|
|
39
|
-
export type
|
|
45
|
+
export type ComponentBoxProps = ComponentsProps & HTMLAttributes<HTMLElement>;
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import React, { PropsWithChildren, useCallback } from 'react';
|
|
2
|
+
|
|
2
3
|
import NSLogoHorizontal from './icons/NSLogoHorizontal';
|
|
3
4
|
import NSLogoMark from './icons/NSLogoMark';
|
|
4
5
|
import NSLogoPoweredBy from './icons/NSLogoPoweredBy';
|
|
5
6
|
import NSLogoStacked from './icons/NSLogoStacked';
|
|
6
7
|
import NSLogoWordMark from './icons/NSWordMark';
|
|
7
8
|
import LogoContainer from './NsBrandContanier.styled';
|
|
9
|
+
|
|
8
10
|
import { NsBrandComponentProps } from './types.d';
|
|
9
11
|
|
|
10
12
|
const VariantMap = {
|
|
@@ -19,6 +21,7 @@ const NsBrandComponent = (props: PropsWithChildren<NsBrandComponentProps>) => {
|
|
|
19
21
|
const { variant = 'stacked', size = '108px', color = 'primary' } = props;
|
|
20
22
|
const renderBrand = useCallback(() => {
|
|
21
23
|
const BrandComponent = VariantMap[variant || 'stacked'];
|
|
24
|
+
// [Todo - Add neutral as FoundationTheme.PaletteColor]
|
|
22
25
|
return <BrandComponent color={color as any} />;
|
|
23
26
|
}, [color, variant]);
|
|
24
27
|
return <LogoContainer size={size}>{renderBrand()}</LogoContainer>;
|
|
@@ -12,12 +12,14 @@ import { NsBreadcrumbProps, ItemProps } from './types.d';
|
|
|
12
12
|
import { useTheme } from '@mui/material/styles';
|
|
13
13
|
import { ColorUtils } from '@nuskin/foundation-theme';
|
|
14
14
|
|
|
15
|
+
const { color } = ColorUtils;
|
|
16
|
+
|
|
15
17
|
const NsBreadcrumb = (props: PropsWithChildren<NsBreadcrumbProps>) => {
|
|
16
18
|
const { items, iconVisibility } = props;
|
|
17
19
|
|
|
18
20
|
const theme = useTheme();
|
|
19
|
-
const neutral80 =
|
|
20
|
-
const neutral60 =
|
|
21
|
+
const neutral80 = color('neutral', '80')(theme);
|
|
22
|
+
const neutral60 = color('neutral', '60')(theme);
|
|
21
23
|
|
|
22
24
|
const getLabel = (item: ItemProps) => {
|
|
23
25
|
if (item?.href) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { render, screen } from '@/test-utils/index';
|
|
3
|
-
import nuskinTheme from '@nuskin/foundation-theme
|
|
3
|
+
import { nuskinTheme } from '@nuskin/foundation-theme';
|
|
4
4
|
import Button from './NsButton';
|
|
5
5
|
|
|
6
6
|
const { palette } = nuskinTheme;
|
|
@@ -8,28 +8,29 @@ const { palette } = nuskinTheme;
|
|
|
8
8
|
describe('<Button />', () => {
|
|
9
9
|
describe(' button primary', () => {
|
|
10
10
|
test('should render default button styles', () => {
|
|
11
|
+
const text = 'Click Me';
|
|
11
12
|
render(
|
|
12
13
|
<Button color="primary" variant="contained">
|
|
13
|
-
|
|
14
|
+
{text}
|
|
14
15
|
</Button>
|
|
15
16
|
);
|
|
16
17
|
|
|
17
|
-
const button = screen.getByText(
|
|
18
|
-
|
|
18
|
+
const button = screen.getByText(text);
|
|
19
19
|
expect(button).toBeInTheDocument();
|
|
20
|
-
expect(button).toHaveStyle(`color: ${palette
|
|
21
|
-
expect(button).toHaveStyle(`background-color: ${palette
|
|
20
|
+
expect(button).toHaveStyle(`color: ${palette?.primary?.contrastText ?? ''}`);
|
|
21
|
+
expect(button).toHaveStyle(`background-color: ${palette?.primary?.main ?? ''}`);
|
|
22
22
|
});
|
|
23
23
|
});
|
|
24
24
|
describe('button secondary', () => {
|
|
25
25
|
test('should render default button styles', () => {
|
|
26
|
+
const text = 'Click Me';
|
|
26
27
|
render(
|
|
27
28
|
<Button color="success" variant="contained" size="large">
|
|
28
|
-
|
|
29
|
+
{text}
|
|
29
30
|
</Button>
|
|
30
31
|
);
|
|
31
32
|
|
|
32
|
-
const button = screen.getByText(
|
|
33
|
+
const button = screen.getByText(text);
|
|
33
34
|
|
|
34
35
|
expect(button).toBeInTheDocument();
|
|
35
36
|
expect(button).toHaveStyle(`color: ${palette.success.contrastText}`);
|
|
@@ -37,7 +38,7 @@ describe('<Button />', () => {
|
|
|
37
38
|
});
|
|
38
39
|
});
|
|
39
40
|
describe('button outline success', () => {
|
|
40
|
-
test('should render outlined
|
|
41
|
+
test('should render outlined success button', () => {
|
|
41
42
|
render(
|
|
42
43
|
<Button color="success" variant="outlined" size="large">
|
|
43
44
|
Click Me
|
|
@@ -48,11 +49,10 @@ describe('<Button />', () => {
|
|
|
48
49
|
|
|
49
50
|
expect(button).toBeInTheDocument();
|
|
50
51
|
expect(button).toHaveStyle(`color: ${palette.success.main}`);
|
|
51
|
-
expect(button).toHaveStyle(`border-color: ${palette.success.main}`);
|
|
52
52
|
});
|
|
53
53
|
});
|
|
54
54
|
describe('button outline accentColorA', () => {
|
|
55
|
-
test('should render outlined
|
|
55
|
+
test('should render outlined success button', () => {
|
|
56
56
|
render(
|
|
57
57
|
<Button color="accentColorA" variant="outlined" size="large">
|
|
58
58
|
Click Me
|
|
@@ -63,7 +63,6 @@ describe('<Button />', () => {
|
|
|
63
63
|
|
|
64
64
|
expect(button).toBeInTheDocument();
|
|
65
65
|
expect(button).toHaveStyle(`color: ${palette?.accentColorA?.main}`);
|
|
66
|
-
expect(button).toHaveStyle(`border-color: ${palette?.accentColorA?.main}`);
|
|
67
66
|
});
|
|
68
67
|
});
|
|
69
68
|
});
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { Button as MUIButton } from '@mui/material';
|
|
2
2
|
|
|
3
3
|
import { styled, ColorUtils, FoundationTheme } from '@nuskin/foundation-theme';
|
|
4
|
-
import { ButtonConfig } from '@nuskin/foundation-theme/src/themes/types/button';
|
|
4
|
+
import { ButtonConfig } from '@nuskin/foundation-theme/src/common/themes/types/button';
|
|
5
|
+
|
|
5
6
|
import { NsButtonProps } from './types.d';
|
|
6
7
|
|
|
7
|
-
const {
|
|
8
|
+
const { buttonColor, paletteColor } = ColorUtils;
|
|
8
9
|
|
|
9
10
|
const getButtonConfig = (theme: FoundationTheme.Theme, attribute: keyof ButtonConfig) => {
|
|
10
11
|
const { config } = theme || {};
|
|
@@ -18,8 +19,8 @@ const StyledButton = styled(MUIButton)<NsButtonProps>`
|
|
|
18
19
|
border: 1px solid ${({ theme }) => getButtonConfig(theme, 'focusColor')} !important;
|
|
19
20
|
}
|
|
20
21
|
:disabled {
|
|
21
|
-
background-color: ${(
|
|
22
|
-
color: ${(
|
|
22
|
+
background-color: ${(btnProps) => buttonColor(btnProps?.color, 'disabled')};
|
|
23
|
+
color: ${(btnProps) => buttonColor(btnProps?.color, 'disabledText')};
|
|
23
24
|
}
|
|
24
25
|
${({ theme: { config }, size }) => {
|
|
25
26
|
return size === 'medium' ? config?.components?.button?.size?.medium?.padding : '';
|
|
@@ -27,37 +28,38 @@ const StyledButton = styled(MUIButton)<NsButtonProps>`
|
|
|
27
28
|
${({ theme: { config }, size }) => {
|
|
28
29
|
return size === 'large' ? config?.components?.button?.size?.large?.padding : '';
|
|
29
30
|
}}
|
|
30
|
-
${({ variant, theme, color = 'primary' }) => {
|
|
31
|
+
${({ variant, theme, color = 'primary', darkModeEnabled }) => {
|
|
31
32
|
return variant === 'contained'
|
|
32
33
|
? `
|
|
33
|
-
background-color: ${
|
|
34
|
+
background-color: ${paletteColor(color)({ theme, darkModeEnabled })};
|
|
35
|
+
color: ${paletteColor(color, 'contrastText')({ theme, darkModeEnabled })} !important;
|
|
34
36
|
:hover {
|
|
35
|
-
background-color: ${
|
|
37
|
+
background-color: ${buttonColor(color, 'hovered')({ theme })};
|
|
36
38
|
}
|
|
37
39
|
:active {
|
|
38
|
-
background-color: ${
|
|
40
|
+
background-color: ${buttonColor(color, 'pressed')({ theme })};
|
|
39
41
|
}
|
|
40
42
|
:focus {
|
|
41
|
-
background-color: ${
|
|
43
|
+
background-color: ${buttonColor(color, 'focused')({ theme })};
|
|
42
44
|
}
|
|
43
45
|
`
|
|
44
46
|
: '';
|
|
45
47
|
}}
|
|
46
|
-
${({ variant, theme, color = 'primary' }) =>
|
|
48
|
+
${({ variant, theme, color = 'primary', darkModeEnabled }) =>
|
|
47
49
|
variant === 'outlined' &&
|
|
48
50
|
`
|
|
49
|
-
border-color: ${
|
|
50
|
-
color: ${
|
|
51
|
+
border-color: ${paletteColor(color)({ theme, darkModeEnabled })};
|
|
52
|
+
color: ${paletteColor(color)({ theme, darkModeEnabled })};
|
|
51
53
|
:hover {
|
|
52
|
-
border-color: ${
|
|
53
|
-
color: ${
|
|
54
|
+
border-color: ${buttonColor(color, 'hovered')({ theme })};
|
|
55
|
+
color: ${paletteColor(color)({ theme, darkModeEnabled })};
|
|
54
56
|
}
|
|
55
57
|
:active {
|
|
56
|
-
border-color: ${
|
|
57
|
-
color: ${
|
|
58
|
+
border-color: ${buttonColor(color, 'pressed')({ theme })};
|
|
59
|
+
color: ${paletteColor(color)({ theme })};
|
|
58
60
|
}
|
|
59
61
|
`}
|
|
60
|
-
${({ variant, theme, color = 'primary' }) =>
|
|
62
|
+
${({ variant, theme, color = 'primary', darkModeEnabled }) =>
|
|
61
63
|
variant === 'text' &&
|
|
62
64
|
`
|
|
63
65
|
text-decoration: underline 1px !important;
|
|
@@ -65,15 +67,15 @@ const StyledButton = styled(MUIButton)<NsButtonProps>`
|
|
|
65
67
|
padding: 0;
|
|
66
68
|
min-width: auto;
|
|
67
69
|
background-color: transparent !important;
|
|
68
|
-
text-decoration-color: ${
|
|
69
|
-
color: ${
|
|
70
|
+
text-decoration-color: ${paletteColor(color)({ theme, darkModeEnabled })};
|
|
71
|
+
color: ${paletteColor(color)({ theme, darkModeEnabled })};
|
|
70
72
|
:hover {
|
|
71
|
-
color: ${
|
|
72
|
-
text-decoration-color: ${
|
|
73
|
+
color: ${buttonColor(color, 'hovered')({ theme })};
|
|
74
|
+
text-decoration-color: ${buttonColor(color, 'hovered')({ theme })};
|
|
73
75
|
}
|
|
74
76
|
:active {
|
|
75
|
-
color: ${
|
|
76
|
-
text-decoration-color: ${
|
|
77
|
+
color: ${paletteColor(color)};
|
|
78
|
+
text-decoration-color: ${buttonColor(color, 'pressed')({ theme })};
|
|
77
79
|
}
|
|
78
80
|
`}
|
|
79
81
|
${({ iconOnly }) =>
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import { PropsWithChildren } from 'react';
|
|
2
|
+
import { useTheme } from '@mui/material/styles';
|
|
3
|
+
|
|
2
4
|
import StyledButton from './NsButton.styled';
|
|
5
|
+
|
|
3
6
|
import { NsButtonProps } from './types.d';
|
|
7
|
+
import { FoundationTheme } from '@nuskin/foundation-theme';
|
|
4
8
|
|
|
5
|
-
const NsButton = ({ children, ...props }: PropsWithChildren<NsButtonProps>) => {
|
|
9
|
+
const NsButton = ({ children, darkModeEnabled = true, ...props }: PropsWithChildren<NsButtonProps>) => {
|
|
10
|
+
const { color } = props;
|
|
11
|
+
const { palette } = useTheme();
|
|
12
|
+
const btnColor = (color && palette?.[color] ? color : 'primary') as FoundationTheme.PaletteColors;
|
|
6
13
|
return (
|
|
7
|
-
<StyledButton {...props} disableRipple>
|
|
14
|
+
<StyledButton {...props} color={btnColor} disableRipple darkModeEnabled={darkModeEnabled}>
|
|
8
15
|
{children}
|
|
9
16
|
</StyledButton>
|
|
10
17
|
);
|
|
@@ -38,8 +38,8 @@ export const BpCheckedIcon = styled(BpIcon)(({ theme: { palette } }) => ({
|
|
|
38
38
|
path: {
|
|
39
39
|
display: 'block',
|
|
40
40
|
},
|
|
41
|
-
background: palette
|
|
42
|
-
color: palette
|
|
41
|
+
background: palette?.primary?.main ?? '#fff',
|
|
42
|
+
color: palette?.primary?.contrastText ?? '#000',
|
|
43
43
|
}));
|
|
44
44
|
|
|
45
45
|
export const StyledLabel = styled('span')<{ $hasDisabled: boolean }>`
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
2
|
import { FoundationTheme } from '@nuskin/foundation-theme';
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
import { AdditionalProps } from '../common.types';
|
|
5
|
+
|
|
6
|
+
export interface DropdownProps extends AdditionalProps {
|
|
5
7
|
selectedValue: string;
|
|
6
8
|
selectedLabel: string;
|
|
7
9
|
onSelect: (arg: string) => void; // eslint-disable-line
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen, act } from '@testing-library/react';
|
|
3
|
+
import NsEmailSimple from './NsEmailSimple';
|
|
4
|
+
import { userEvent } from '@storybook/testing-library';
|
|
5
|
+
import { emailPattern } from '../validations/regex';
|
|
6
|
+
|
|
7
|
+
describe('NsEmailSimple', () => {
|
|
8
|
+
let mockCheckEmail = jest.fn().mockResolvedValue([true, false]);
|
|
9
|
+
let propsData = {
|
|
10
|
+
disabled: false,
|
|
11
|
+
label: 'Email',
|
|
12
|
+
required: true,
|
|
13
|
+
maxLength: 50,
|
|
14
|
+
invalidEmailMessage: 'Please enter a valid email',
|
|
15
|
+
dupedEmailMessage: 'Email ID already exists',
|
|
16
|
+
requiredErrorMessage: 'Email field is required',
|
|
17
|
+
checkEmail: mockCheckEmail,
|
|
18
|
+
};
|
|
19
|
+
it('renders email field with invalid regex error', async () => {
|
|
20
|
+
render(<NsEmailSimple {...propsData} />);
|
|
21
|
+
const emailField = (await screen.findByTestId('email-field-test')) as HTMLInputElement;
|
|
22
|
+
expect(emailField).toBeInTheDocument;
|
|
23
|
+
const inputElement = emailField.querySelector('input');
|
|
24
|
+
if (!inputElement) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
act(() => {
|
|
28
|
+
userEvent.type(inputElement, 'test');
|
|
29
|
+
userEvent.tab();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
expect(inputElement).toHaveValue('test');
|
|
33
|
+
|
|
34
|
+
act(() => {
|
|
35
|
+
userEvent.clear(inputElement);
|
|
36
|
+
userEvent.type(inputElement, 'test@gm');
|
|
37
|
+
userEvent.tab();
|
|
38
|
+
});
|
|
39
|
+
expect(inputElement).toHaveValue('test@gm');
|
|
40
|
+
|
|
41
|
+
mockCheckEmail = jest.fn().mockResolvedValue([true, true]);
|
|
42
|
+
render(<NsEmailSimple {...propsData} />);
|
|
43
|
+
act(() => {
|
|
44
|
+
userEvent.clear(inputElement);
|
|
45
|
+
userEvent.type(inputElement, 'test!gmail');
|
|
46
|
+
userEvent.tab();
|
|
47
|
+
});
|
|
48
|
+
expect(inputElement).toHaveValue('test!gmail');
|
|
49
|
+
act(() => {
|
|
50
|
+
userEvent.clear(inputElement);
|
|
51
|
+
userEvent.tab();
|
|
52
|
+
});
|
|
53
|
+
expect(inputElement).toHaveValue('');
|
|
54
|
+
|
|
55
|
+
mockCheckEmail = jest.fn().mockResolvedValue([true, false]);
|
|
56
|
+
render(<NsEmailSimple {...propsData} />);
|
|
57
|
+
act(() => {
|
|
58
|
+
userEvent.clear(inputElement);
|
|
59
|
+
userEvent.type(inputElement, 'test@gmail.co');
|
|
60
|
+
userEvent.tab();
|
|
61
|
+
});
|
|
62
|
+
expect(inputElement).toHaveValue('test@gmail.co');
|
|
63
|
+
});
|
|
64
|
+
it('renders email field with invalid error', async () => {
|
|
65
|
+
let mockCheckEmail1 = jest.fn().mockResolvedValue([false, false]);
|
|
66
|
+
let propsData1 = {
|
|
67
|
+
disabled: false,
|
|
68
|
+
label: 'Email',
|
|
69
|
+
required: true,
|
|
70
|
+
maxLength: 50,
|
|
71
|
+
invalidEmailMessage: 'Please enter a valid email',
|
|
72
|
+
dupedEmailMessage: 'Email ID already exists',
|
|
73
|
+
requiredErrorMessage: 'Email field is required',
|
|
74
|
+
checkEmail: mockCheckEmail1,
|
|
75
|
+
};
|
|
76
|
+
render(<NsEmailSimple {...propsData1} />);
|
|
77
|
+
const emailField = (await screen.findByTestId('email-field-test')) as HTMLInputElement;
|
|
78
|
+
expect(emailField).toBeInTheDocument;
|
|
79
|
+
const inputElement = emailField.querySelector('input');
|
|
80
|
+
if (!inputElement) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
act(() => {
|
|
84
|
+
userEvent.clear(inputElement);
|
|
85
|
+
userEvent.type(inputElement, 'test@gmail.com');
|
|
86
|
+
userEvent.tab();
|
|
87
|
+
});
|
|
88
|
+
expect(inputElement).toHaveValue('test@gmail.com');
|
|
89
|
+
expect(inputElement.value).toMatch(emailPattern);
|
|
90
|
+
});
|
|
91
|
+
it('renders email field without error', async () => {
|
|
92
|
+
let mockCheckEmail1 = jest.fn().mockResolvedValue([true, true]);
|
|
93
|
+
let propsData1 = {
|
|
94
|
+
disabled: false,
|
|
95
|
+
label: 'Email',
|
|
96
|
+
required: true,
|
|
97
|
+
maxLength: 50,
|
|
98
|
+
invalidEmailMessage: 'Please enter a valid email',
|
|
99
|
+
dupedEmailMessage: 'Email ID already exists',
|
|
100
|
+
requiredErrorMessage: 'Email field is required',
|
|
101
|
+
checkEmail: mockCheckEmail1,
|
|
102
|
+
};
|
|
103
|
+
render(<NsEmailSimple {...propsData1} />);
|
|
104
|
+
const emailField = (await screen.findByTestId('email-field-test')) as HTMLInputElement;
|
|
105
|
+
expect(emailField).toBeInTheDocument;
|
|
106
|
+
const inputElement = emailField.querySelector('input');
|
|
107
|
+
if (!inputElement) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
act(() => {
|
|
111
|
+
userEvent.clear(inputElement);
|
|
112
|
+
userEvent.type(inputElement, 'test@gmail.com');
|
|
113
|
+
userEvent.tab();
|
|
114
|
+
});
|
|
115
|
+
expect(inputElement).toHaveValue('test@gmail.com');
|
|
116
|
+
expect(inputElement.value).toMatch(emailPattern);
|
|
117
|
+
});
|
|
118
|
+
it('renders email field with API error', async () => {
|
|
119
|
+
let mockCheckEmail1 = jest.fn().mockResolvedValue(undefined);
|
|
120
|
+
let propsData1 = {
|
|
121
|
+
disabled: false,
|
|
122
|
+
label: 'Email',
|
|
123
|
+
required: true,
|
|
124
|
+
maxLength: 50,
|
|
125
|
+
invalidEmailMessage: 'Please enter a valid email',
|
|
126
|
+
dupedEmailMessage: 'Email ID already exists',
|
|
127
|
+
requiredErrorMessage: 'Email field is required',
|
|
128
|
+
checkEmail: mockCheckEmail1,
|
|
129
|
+
};
|
|
130
|
+
render(<NsEmailSimple {...propsData1} />);
|
|
131
|
+
const emailField = (await screen.findByTestId('email-field-test')) as HTMLInputElement;
|
|
132
|
+
expect(emailField).toBeInTheDocument;
|
|
133
|
+
const inputElement = emailField.querySelector('input');
|
|
134
|
+
if (!inputElement) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
act(() => {
|
|
138
|
+
userEvent.clear(inputElement);
|
|
139
|
+
userEvent.type(inputElement, 'test@gmail.com');
|
|
140
|
+
userEvent.tab();
|
|
141
|
+
});
|
|
142
|
+
expect(inputElement).toHaveValue('test@gmail.com');
|
|
143
|
+
expect(inputElement.value).toMatch(emailPattern);
|
|
144
|
+
});
|
|
145
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { StoryObj, Meta } from '@storybook/react';
|
|
2
|
+
import NsEmailSimple from './NsEmailSimple';
|
|
3
|
+
import { within } from '@storybook/testing-library';
|
|
4
|
+
import { expect } from '@storybook/jest';
|
|
5
|
+
import { withCenterDecorator } from '@/stories/decorators/withCenterDecorator';
|
|
6
|
+
|
|
7
|
+
async function checkEmail() {
|
|
8
|
+
const isServiceValid = Promise.resolve(true);
|
|
9
|
+
const isEmailAvailable = Promise.resolve(true);
|
|
10
|
+
return [isServiceValid, isEmailAvailable];
|
|
11
|
+
}
|
|
12
|
+
type ComponentType = typeof NsEmailSimple;
|
|
13
|
+
type Story = StoryObj<ComponentType>;
|
|
14
|
+
type MetaStory = Meta<ComponentType>;
|
|
15
|
+
|
|
16
|
+
export default {
|
|
17
|
+
component: NsEmailSimple,
|
|
18
|
+
title: 'Components/Inputs/NsEmailSimple',
|
|
19
|
+
decorators: [withCenterDecorator],
|
|
20
|
+
play: ({ canvasElement }) => {
|
|
21
|
+
const canvas = within(canvasElement);
|
|
22
|
+
const el = canvas.getByTestId('email-field-test');
|
|
23
|
+
expect(el).toBeInTheDocument();
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
argTypes: {
|
|
27
|
+
disabled: {
|
|
28
|
+
description: 'Indicates whether the email field is enabled/disabled',
|
|
29
|
+
},
|
|
30
|
+
label: {
|
|
31
|
+
description: 'Label of the email component',
|
|
32
|
+
},
|
|
33
|
+
required: {
|
|
34
|
+
description: 'Indicates whether the email field is mandatory or not',
|
|
35
|
+
},
|
|
36
|
+
maxLength: {
|
|
37
|
+
description: 'Maximum number of characters allowed',
|
|
38
|
+
},
|
|
39
|
+
invalidEmailMessage: {
|
|
40
|
+
description: 'Displayed when an invalid email is entered',
|
|
41
|
+
},
|
|
42
|
+
dupedEmailMessage: {
|
|
43
|
+
description: 'Displayed when a duplicate email is entered',
|
|
44
|
+
},
|
|
45
|
+
requiredErrorMessage: {
|
|
46
|
+
description: 'Displayed when email is not entered',
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
args: {
|
|
50
|
+
disabled: false,
|
|
51
|
+
label: 'Email',
|
|
52
|
+
required: true,
|
|
53
|
+
maxLength: 50,
|
|
54
|
+
invalidEmailMessage: 'Please enter a valid email',
|
|
55
|
+
dupedEmailMessage: 'Email ID already exists',
|
|
56
|
+
requiredErrorMessage: 'Email field is required',
|
|
57
|
+
checkEmail: checkEmail,
|
|
58
|
+
},
|
|
59
|
+
} as MetaStory;
|
|
60
|
+
|
|
61
|
+
export const NsEmail: Story = {};
|