@nuskin/foundation-ui-components 1.0.0-develop.6 → 1.0.0-develop.8
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 +17 -12
- package/src/common/accordion/NsAccordion.tsx +30 -40
- package/src/common/accordion/NsAccordionHeader.tsx +27 -7
- package/src/common/accordion/NsAccordionItem.tsx +16 -4
- package/src/common/accordion/NsCollapse.tsx +71 -35
- package/src/common/accordion/NsToggleSection.tsx +14 -3
- package/src/common/accordion/ToggleSection.styled.tsx +11 -7
- package/src/common/accordion/colorMap.ts +117 -0
- package/src/common/accordion/enums.ts +8 -0
- package/src/common/accordion/index.ts +1 -0
- package/src/common/accordion/specs/NsAccordion.spec.tsx +100 -31
- package/src/common/accordion/specs/NsAccordionHeader.spec.tsx +77 -10
- package/src/common/accordion/specs/NsAccordionItem.spec.tsx +23 -10
- package/src/common/accordion/specs/NsCollapse.spec.tsx +106 -17
- package/src/common/accordion/specs/NsToggleSection.spec.tsx +46 -14
- package/src/common/accordion/stories/NsAccordion.stories.tsx +32 -14
- package/src/common/accordion/stories/NsAccordionHeader.stories.tsx +4 -1
- package/src/common/accordion/stories/NsAccordionItem.stories.tsx +3 -3
- package/src/common/accordion/stories/NsCollapse.stories.tsx +3 -3
- package/src/common/accordion/stories/NsToggleSection.stories.tsx +2 -3
- package/src/common/accordion/stories/data.tsx +157 -19
- package/src/common/accordion/types.d.ts +56 -49
- package/src/common/alert/Header.tsx +29 -0
- package/src/common/alert/NsAlert.spec.tsx +46 -13
- package/src/common/alert/NsAlert.stories.tsx +3 -3
- package/src/common/alert/{NsAlert.styled.ts → NsAlert.styled.tsx} +22 -22
- package/src/common/alert/NsAlert.tsx +22 -87
- package/src/common/alert/types.d.ts +7 -7
- package/src/common/auth-dropdown/NsAuthDropdown.spec.tsx +23 -5
- package/src/common/auth-dropdown/NsAuthDropdown.stories.tsx +1 -1
- package/src/common/auth-dropdown/NsAuthDropdown.styled.ts +11 -7
- package/src/common/auth-dropdown/NsAuthDropdown.tsx +4 -4
- package/src/common/autocomplete/NsAutocomplete.spec.tsx +29 -6
- package/src/common/autocomplete/NsAutocomplete.stories.tsx +1 -1
- package/src/common/autocomplete/NsAutocomplete.tsx +18 -8
- package/src/common/autocomplete/mock/MockUI.tsx +1 -0
- package/src/common/banner/NsBanner.spec.tsx +12 -3
- package/src/common/banner/NsBanner.stories.tsx +1 -1
- package/src/common/brand-components/NsBrand.stories.tsx +1 -1
- package/src/common/brand-components/NsBrandComponent.tsx +2 -2
- package/src/common/breadcrumb/NsBreadcrumb.spec.tsx +8 -3
- package/src/common/breadcrumb/NsBreadcrumb.stories.tsx +1 -1
- package/src/common/breadcrumb/data.ts +8 -5
- package/src/common/button/NsButton.spec.tsx +65 -49
- package/src/common/button/NsButton.stories.tsx +59 -2
- package/src/common/button/NsButton.styled.tsx +49 -66
- package/src/common/button/NsButton.tsx +1 -2
- package/src/common/button/helper.ts +82 -0
- package/src/common/button/types.d.ts +2 -0
- package/src/common/card/stories/NsBgCard.stories.tsx +1 -1
- package/src/common/card/stories/NsBrandProductCard.stories.tsx +1 -1
- package/src/common/card/stories/NsFfgCard.stories.tsx +5 -2
- package/src/common/card/styled/NsFfgStyled.tsx +1 -1
- package/src/common/carousal/stories/NSCarousal.stories.tsx +1 -1
- package/src/common/carousal/stories/NsScrollingCarousal.stories.tsx +1 -1
- package/src/common/cart-icon/NsCartIcon.stories.tsx +1 -1
- package/src/common/checkbox/NsCheckbox.stories.tsx +1 -1
- package/src/common/checkbox/NsCheckbox.styled.tsx +15 -16
- package/src/common/checkbox/NsCheckbox.tsx +1 -1
- package/src/common/column-control/stories/DivArray.stories.tsx +1 -1
- package/src/common/column-control/stories/NsColumnControl.stories.tsx +1 -1
- package/src/common/column-control/stories/NsFlexColumns.stories.tsx +1 -1
- package/src/common/column-control/stories/NsGridColumns.stories.tsx +1 -1
- package/src/common/column-control/styled/common.ts +1 -1
- package/src/common/column-control/utils.ts +4 -4
- package/src/common/common.types.d.ts +11 -1
- package/src/common/dropdown/NsDropdown.styled.tsx +23 -18
- package/src/common/dropdown/stories/NsDropdown.stories.tsx +1 -1
- package/src/common/dropdown/stories/NsDropdownCountry.stories.tsx +1 -1
- package/src/common/flag/NsFlag.stories.tsx +1 -1
- package/src/common/hooks/index.ts +2 -0
- package/src/common/hooks/specs/useWindowSize.spec.tsx +19 -0
- package/src/common/hooks/useAccordionMap.tsx +39 -0
- package/src/common/hooks/useWindowSize.tsx +30 -0
- package/src/common/icon/NsIcon.stories.tsx +1 -1
- package/src/common/icon/NsIconVariants.ts +1 -0
- package/src/common/image/NsImage.spec.tsx +56 -6
- package/src/common/image/NsImage.stories.tsx +1 -1
- package/src/common/image/NsImage.tsx +23 -11
- package/src/common/image/NsNextImage.stories.tsx +1 -1
- package/src/common/index.ts +6 -2
- package/src/common/input/InputWrapper.tsx +12 -7
- package/src/common/input/NsInputField.tsx +1 -1
- package/src/common/label/NsLabel.stories.tsx +1 -1
- package/src/common/link/NsLink.stories.tsx +1 -1
- package/src/common/modal/NsModal.spec.tsx +46 -0
- package/src/common/modal/NsModal.styled.tsx +28 -0
- package/src/common/modal/NsModal.tsx +37 -0
- package/src/common/modal/helper-component/NsModalContentBox.tsx +32 -0
- package/src/common/modal/helper-component/NsModalDefault.tsx +25 -0
- package/src/common/modal/helper-component/NsModalStatus.tsx +15 -0
- package/src/common/modal/helper-component/NsModalStatusIcon.tsx +14 -0
- package/src/common/modal/helper-component/NsModalTitle.tsx +26 -0
- package/src/common/modal/index.ts +2 -0
- package/src/common/modal/model/NsModalVariantMap.tsx +24 -0
- package/src/common/modal/stories/NsModal.stories.tsx +86 -0
- package/src/common/modal/stories/mock.tsx +46 -0
- package/src/common/modal/types.ts +56 -0
- package/src/common/nav-product-card/NsNavProductCard.stories.tsx +1 -1
- package/src/common/nav-tab/NsNavTab.stories.tsx +1 -1
- package/src/common/radiogroup/NsRadioGroup.spec.tsx +24 -25
- package/src/common/radiogroup/NsRadioGroup.stories.tsx +14 -32
- package/src/common/radiogroup/NsRadioGroup.styled.tsx +17 -15
- package/src/common/radiogroup/NsRadioGroup.tsx +6 -5
- package/src/common/search-bar/NsSearchBar.tsx +1 -3
- package/src/common/select/NsSelect.spec.tsx +10 -17
- package/src/common/select/NsSelect.styled.tsx +53 -34
- package/src/common/select/stories/NsSelect.stories.tsx +11 -11
- package/src/common/select/types.d.ts +3 -3
- package/src/common/spinner/NsSpinner.spec.tsx +21 -0
- package/src/common/spinner/NsSpinner.stories.tsx +26 -0
- package/src/common/spinner/NsSpinner.styled.tsx +344 -0
- package/src/common/spinner/NsSpinner.tsx +14 -0
- package/src/common/spinner/icons/Spinner.tsx +161 -0
- package/src/common/spinner/index.ts +2 -0
- package/src/common/spinner/types.ts +5 -0
- package/src/common/table/NsTable.stories.tsx +1 -1
- package/src/common/table/NsTable.tsx +32 -49
- package/src/common/table/NsTableHeader.tsx +30 -51
- package/src/common/text-strip/NsTextStrip.stories.tsx +1 -1
- package/src/common/typography/NsTypography.stories.tsx +1 -1
- package/src/common/util.ts +1 -0
- package/src/common/utility/Show.tsx +13 -0
- package/src/common/utility/index.ts +1 -0
- package/src/common/videos/NsVideo.stories.tsx +1 -1
- package/src/common/videos/types.d.ts +1 -1
- /package/src/common/brand-components/{NsBrandContanier.styled.tsx → NsBrandContainer.styled.tsx} +0 -0
|
@@ -54,7 +54,7 @@ const NsCheckbox = (props: PropsWithChildren<NsCheckboxProps>) => {
|
|
|
54
54
|
() => (
|
|
55
55
|
<NsTypography variant="p-l">
|
|
56
56
|
<StyledFlagLabel {...flag} size={size} testId={`${name}-flag-label`} />
|
|
57
|
-
<StyledLabel className="checkbox-label-text"
|
|
57
|
+
<StyledLabel className="checkbox-label-text" isDisabled={disabled}>
|
|
58
58
|
{label}
|
|
59
59
|
</StyledLabel>
|
|
60
60
|
</NsTypography>
|
|
@@ -9,7 +9,7 @@ type Story = StoryObj<ComponentType>;
|
|
|
9
9
|
type MetaStory = Meta<ComponentType>;
|
|
10
10
|
|
|
11
11
|
export default {
|
|
12
|
-
title: 'Components/Column/NsColumnControl',
|
|
12
|
+
title: 'Components/Data Display/Column/NsColumnControl',
|
|
13
13
|
component: NsColumnControl,
|
|
14
14
|
decorators: [withCenterDecorator],
|
|
15
15
|
argTypes: {
|
|
@@ -9,7 +9,7 @@ type Story = StoryObj<ComponentType>;
|
|
|
9
9
|
type MetaStory = Meta<ComponentType>;
|
|
10
10
|
|
|
11
11
|
export default {
|
|
12
|
-
title: 'Components/Column/NsFlexColumns',
|
|
12
|
+
title: 'Components/Data Display/Column/NsFlexColumns',
|
|
13
13
|
component: NsFlexColumns,
|
|
14
14
|
decorators: [withCenterDecorator],
|
|
15
15
|
argTypes: {
|
|
@@ -9,7 +9,7 @@ type Story = StoryObj<ComponentType>;
|
|
|
9
9
|
type MetaStory = Meta<ComponentType>;
|
|
10
10
|
|
|
11
11
|
export default {
|
|
12
|
-
title: 'Components/Column/NsGridColumns',
|
|
12
|
+
title: 'Components/Data Display/Column/NsGridColumns',
|
|
13
13
|
component: NsGridColumns,
|
|
14
14
|
decorators: [withCenterDecorator],
|
|
15
15
|
argTypes: {
|
|
@@ -21,7 +21,7 @@ export const commonStyles = {
|
|
|
21
21
|
['1','2'] => ['27%','53%']
|
|
22
22
|
*/
|
|
23
23
|
export function getPercentArray(fractionArray?: string[] | number[]) {
|
|
24
|
-
|
|
24
|
+
const numbers = fractionArray?.map((numberString) => +numberString)?.filter((num) => 0 < num && num < 10) ?? [];
|
|
25
25
|
const sum = numbers.reduce((currentSum, currentNumber) => currentSum + currentNumber, 0);
|
|
26
26
|
return numbers.map((currentNumber) => ((currentNumber / sum) * 80).toFixed(2) + '%');
|
|
27
27
|
}
|
|
@@ -40,15 +40,15 @@ const sizeKeyMap: Record<ValueOf<typeof sizeKeyPrefix>, string> = {
|
|
|
40
40
|
* @returns {fractions:['1','1','1'], tabletFractions:['1','1'], mobileFractions:['1'] }
|
|
41
41
|
*/
|
|
42
42
|
export const getColumnSizes = (inputColumnClass: string): sizeObjectType => {
|
|
43
|
-
|
|
43
|
+
const columnClass = inputColumnClass.trim();
|
|
44
44
|
const validClasses = columnClass
|
|
45
45
|
.split(' ')
|
|
46
46
|
.filter((e) => Object.values(sizeKeyPrefix).some((prefix) => e.startsWith(prefix)));
|
|
47
47
|
const sizeObject: sizeObjectType = validClasses.reduce((acc: any, current: string) => {
|
|
48
|
-
for (
|
|
48
|
+
for (const key of Object.values(sizeKeyPrefix)) {
|
|
49
49
|
if (current.startsWith(key)) {
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
const interClass = current.replace(key, '');
|
|
51
|
+
const sizes = interClass.split('-').filter((e) => /^[1-9]$/.test(e));
|
|
52
52
|
acc[sizeKeyMap[key]] = sizes;
|
|
53
53
|
break;
|
|
54
54
|
}
|
|
@@ -1,12 +1,22 @@
|
|
|
1
|
+
import { FoundationTheme } from '@nuskin/foundation-theme';
|
|
2
|
+
|
|
1
3
|
export type ContentStackLiveTags = {
|
|
2
4
|
'data-cslp'?: string;
|
|
3
5
|
};
|
|
4
6
|
|
|
5
7
|
export type AdditionalProps = {
|
|
6
|
-
darkModeEnabled?: boolean;
|
|
7
8
|
additionalProps?: {
|
|
8
9
|
ariaLabel?: string;
|
|
9
10
|
testid?: string;
|
|
10
11
|
className?: string;
|
|
12
|
+
'data-testid'?: string;
|
|
11
13
|
} & ContentStackLiveTags;
|
|
14
|
+
} & DarkModeEnabledOption;
|
|
15
|
+
|
|
16
|
+
export type Themed = {
|
|
17
|
+
theme: FoundationTheme.Theme;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type DarkModeEnabledOption = {
|
|
21
|
+
darkModeEnabled?: boolean;
|
|
12
22
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { styled, ColorUtils, FoundationTheme } from '@nuskin/foundation-theme';
|
|
2
|
-
|
|
2
|
+
import { Themed } from '../common.types';
|
|
3
|
+
const { color, paletteColor, isDarkMode } = ColorUtils;
|
|
3
4
|
|
|
4
5
|
export const DropdownContainer = styled.div<{ disabled: boolean }>`
|
|
5
6
|
position: relative;
|
|
@@ -7,15 +8,22 @@ export const DropdownContainer = styled.div<{ disabled: boolean }>`
|
|
|
7
8
|
opacity: ${({ disabled }) => (disabled ? '0.5' : '1')};
|
|
8
9
|
`;
|
|
9
10
|
|
|
11
|
+
/** this function also used for SelectHead */
|
|
12
|
+
export const getDropdownHeadBorder = ({ type, theme }: { type: string } & Themed) => {
|
|
13
|
+
if (type === 'outlined') {
|
|
14
|
+
const borderColor = color('primary', 'black')({ theme });
|
|
15
|
+
return `1px solid ${borderColor}`;
|
|
16
|
+
}
|
|
17
|
+
return 'none';
|
|
18
|
+
};
|
|
10
19
|
export const DropdownHead = styled.div<{ type: string; disabled: boolean }>`
|
|
11
20
|
cursor: ${({ disabled }) => (disabled ? 'inherit' : 'pointer')};
|
|
12
21
|
padding: 5px 10px;
|
|
13
22
|
display: flex;
|
|
14
23
|
justify-content: space-between;
|
|
15
24
|
align-items: center;
|
|
16
|
-
border: ${
|
|
17
|
-
|
|
18
|
-
color: ${({ theme }) => getPaletteColor(theme, 'primary')};
|
|
25
|
+
border: ${getDropdownHeadBorder}
|
|
26
|
+
color: ${paletteColor('primary')};
|
|
19
27
|
`;
|
|
20
28
|
|
|
21
29
|
export const DropdownButton = styled.div`
|
|
@@ -25,27 +33,19 @@ export const DropdownButton = styled.div`
|
|
|
25
33
|
export const DropdownList = styled.div<{ borderColor: FoundationTheme.PaletteColors }>`
|
|
26
34
|
position: absolute;
|
|
27
35
|
z-index: 100;
|
|
28
|
-
border: 1px solid ${({ borderColor, theme }) =>
|
|
36
|
+
border: 1px solid ${({ borderColor, theme }) => paletteColor(borderColor)({ theme })};
|
|
29
37
|
padding: 0;
|
|
30
38
|
margin: 0;
|
|
31
39
|
width: 100%;
|
|
32
|
-
color: ${(
|
|
33
|
-
background-color: ${(
|
|
40
|
+
color: ${paletteColor('primary', 'main')};
|
|
41
|
+
background-color: ${paletteColor('primary', 'contrastText')};
|
|
34
42
|
`;
|
|
35
|
-
const getIsLight = (theme: FoundationTheme.Theme) => {
|
|
36
|
-
const { palette = { primary: { main: '', contrastText: '' } } } = theme;
|
|
37
|
-
return palette.primary.main === getAppColor(theme, 'primary', 'black');
|
|
38
|
-
};
|
|
39
43
|
|
|
40
44
|
const getDividerColor = (theme: FoundationTheme.Theme) => {
|
|
41
|
-
|
|
42
|
-
const dividerColor = isLight ? getAppColor(theme, 'neutral', '30') : getAppColor(theme, 'neutral', '20');
|
|
43
|
-
return dividerColor;
|
|
45
|
+
return color('neutral', isDarkMode({ theme }) ? '20' : '30')({ theme });
|
|
44
46
|
};
|
|
45
47
|
const getListBgColor = (theme: FoundationTheme.Theme) => {
|
|
46
|
-
|
|
47
|
-
const bgColor = isLight ? getAppColor(theme, 'accentColorB', '10') : getAppColor(theme, 'accentColorB', '50');
|
|
48
|
-
return bgColor;
|
|
48
|
+
return color('accentColorB', isDarkMode({ theme }) ? '50' : '10')({ theme });
|
|
49
49
|
};
|
|
50
50
|
export const ListItemContainer = styled.div<{ showListDivider: boolean }>`
|
|
51
51
|
cursor: pointer;
|
|
@@ -61,7 +61,12 @@ export const ListItemContainer = styled.div<{ showListDivider: boolean }>`
|
|
|
61
61
|
}
|
|
62
62
|
`;
|
|
63
63
|
|
|
64
|
+
const getBorder = ({ isSelected, theme }: { isSelected: boolean } & Themed) => {
|
|
65
|
+
const borderColor = paletteColor('primary')({ theme });
|
|
66
|
+
return isSelected ? `2px solid ${borderColor}` : 'none';
|
|
67
|
+
};
|
|
68
|
+
|
|
64
69
|
export const ListItem = styled.span<{ isSelected: boolean }>`
|
|
65
70
|
padding: 5px 10px;
|
|
66
|
-
border-left: ${
|
|
71
|
+
border-left: ${getBorder};
|
|
67
72
|
`;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { renderHook, act } from '@testing-library/react-hooks';
|
|
2
|
+
import useWindowSize from '../useWindowSize';
|
|
3
|
+
|
|
4
|
+
describe('useWindowSize', () => {
|
|
5
|
+
it('should return the window size', () => {
|
|
6
|
+
const { result } = renderHook(useWindowSize);
|
|
7
|
+
expect(result.current).toEqual({ width: window.innerWidth, height: window.innerHeight });
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it('should update the window size when the window is resized', () => {
|
|
11
|
+
const { result } = renderHook(() => useWindowSize());
|
|
12
|
+
act(() => {
|
|
13
|
+
window.innerWidth = 500;
|
|
14
|
+
window.innerHeight = 500;
|
|
15
|
+
window.dispatchEvent(new Event('resize'));
|
|
16
|
+
});
|
|
17
|
+
expect(result.current).toEqual({ width: 500, height: 500 });
|
|
18
|
+
});
|
|
19
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { useState, useEffect, useCallback } from 'react';
|
|
2
|
+
|
|
3
|
+
type ItemMap = Record<string, boolean>;
|
|
4
|
+
type UseAccordionProp = {
|
|
5
|
+
allowMultipleExpanded?: boolean;
|
|
6
|
+
defaultOpenIds?: string[];
|
|
7
|
+
};
|
|
8
|
+
const reducer = (map: ItemMap, id: string) => {
|
|
9
|
+
map[id] = true;
|
|
10
|
+
return map;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const useAccordionMap = ({ allowMultipleExpanded, defaultOpenIds }: UseAccordionProp) => {
|
|
14
|
+
const mapSetter = useCallback(() => {
|
|
15
|
+
let ids = defaultOpenIds;
|
|
16
|
+
if (!allowMultipleExpanded) {
|
|
17
|
+
ids = ids?.slice(0, 1) ?? [];
|
|
18
|
+
}
|
|
19
|
+
return ids?.reduce(reducer, {}) ?? {};
|
|
20
|
+
}, [defaultOpenIds, allowMultipleExpanded]);
|
|
21
|
+
|
|
22
|
+
const [itemStateMap, setItemMap] = useState<ItemMap>(mapSetter);
|
|
23
|
+
|
|
24
|
+
const toggleOpen = (index: string | number) => {
|
|
25
|
+
const key = index + '';
|
|
26
|
+
if (allowMultipleExpanded) {
|
|
27
|
+
setItemMap((prevState) => ({ ...prevState, [key]: !prevState[key] }));
|
|
28
|
+
} else {
|
|
29
|
+
setItemMap({ [key]: !itemStateMap[index] });
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
setItemMap(mapSetter);
|
|
35
|
+
}, [mapSetter]);
|
|
36
|
+
return { itemStateMap, toggleOpen };
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export default useAccordionMap;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
const useWindowSize = () => {
|
|
4
|
+
const [windowSize, setWindowSize] = useState({
|
|
5
|
+
width: window.innerWidth,
|
|
6
|
+
height: window.innerHeight,
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
function handleResize() {
|
|
11
|
+
setWindowSize({
|
|
12
|
+
width: window?.innerWidth || 300,
|
|
13
|
+
height: window?.innerHeight || 300,
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
if (typeof window !== 'undefined') {
|
|
17
|
+
window.addEventListener('resize', handleResize);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return () => {
|
|
21
|
+
if (typeof window !== 'undefined') {
|
|
22
|
+
window.removeEventListener('resize', handleResize);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
}, []);
|
|
26
|
+
|
|
27
|
+
return windowSize;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export default useWindowSize;
|
|
@@ -1,10 +1,60 @@
|
|
|
1
1
|
import { render, screen } from '@/test-utils/index';
|
|
2
|
-
import NsImage from '
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
2
|
+
import { NsImage } from '../';
|
|
3
|
+
|
|
4
|
+
describe('NsImage component', () => {
|
|
5
|
+
it('1. renders', () => {
|
|
6
|
+
const alt = 'test image';
|
|
7
|
+
render(<NsImage src="image.jpg" width={300} height={300} alt={alt} />);
|
|
8
|
+
const imgElement = screen.getByAltText(alt);
|
|
8
9
|
expect(imgElement).toBeInTheDocument();
|
|
9
10
|
});
|
|
11
|
+
|
|
12
|
+
it('2. renders with correct src and alt attributes', () => {
|
|
13
|
+
const src = 'image.jpg';
|
|
14
|
+
const alt = 'test image';
|
|
15
|
+
const { getByAltText } = render(<NsImage src={src} alt={alt} />);
|
|
16
|
+
const image = getByAltText(alt);
|
|
17
|
+
expect(image).toHaveAttribute('src', src);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('3. renders with width and height if variant is not provided', () => {
|
|
21
|
+
const { getByAltText } = render(<NsImage src="image.jpg" alt="test image" width={300} height={300} />);
|
|
22
|
+
const image = getByAltText('test image');
|
|
23
|
+
expect(image).toHaveAttribute('width', '300');
|
|
24
|
+
expect(image).toHaveAttribute('height', '300');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('4. renders with correct width and height based on variant', () => {
|
|
28
|
+
const { getByAltText } = render(<NsImage src="image.jpg" alt="test image" variant="icon" />);
|
|
29
|
+
const image = getByAltText('test image');
|
|
30
|
+
expect(image).toHaveAttribute('width', '60');
|
|
31
|
+
expect(image).toHaveAttribute('height', '60');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('5. renders with correct width and height if variant is "fill"', () => {
|
|
35
|
+
const { getByAltText } = render(<NsImage src="image.jpg" alt="test image" variant="fill" />);
|
|
36
|
+
const image = getByAltText('test image');
|
|
37
|
+
expect(image).toHaveAttribute('width', '100%');
|
|
38
|
+
expect(image).toHaveAttribute('height', 'auto');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('6. renders with correct width and height if variant is "award"', () => {
|
|
42
|
+
const { getByAltText } = render(<NsImage src="image.jpg" alt="test image" variant="award" />);
|
|
43
|
+
const image = getByAltText('test image');
|
|
44
|
+
expect(image).toHaveAttribute('width', '130');
|
|
45
|
+
expect(image).toHaveAttribute('height', '130');
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('7. renders with correct width and height if variant is "product"', () => {
|
|
49
|
+
const { getByAltText } = render(<NsImage src="image.jpg" alt="test image" variant="product" />);
|
|
50
|
+
const image = getByAltText('test image');
|
|
51
|
+
expect(image).toHaveAttribute('width', '300');
|
|
52
|
+
expect(image).toHaveAttribute('height', '300');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('8. renders with additional props', () => {
|
|
56
|
+
const { getByAltText } = render(<NsImage src="image.jpg" alt="test image" className="custom-image" />);
|
|
57
|
+
const image = getByAltText('test image');
|
|
58
|
+
expect(image).toHaveClass('custom-image');
|
|
59
|
+
});
|
|
10
60
|
});
|
|
@@ -2,18 +2,30 @@ import { NsImageProps } from './types.d';
|
|
|
2
2
|
|
|
3
3
|
// eslint-disable-next-line no-unused-vars
|
|
4
4
|
const NsImage = ({ src, width, height, alt, variant, ...rest }: NsImageProps) => {
|
|
5
|
-
let cHeight
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
5
|
+
let cHeight, cWidth;
|
|
6
|
+
switch (variant) {
|
|
7
|
+
case 'product':
|
|
8
|
+
cHeight = 300;
|
|
9
|
+
cWidth = 300;
|
|
10
|
+
break;
|
|
11
|
+
case 'icon':
|
|
12
|
+
cHeight = 60;
|
|
13
|
+
cWidth = 60;
|
|
14
|
+
break;
|
|
15
|
+
case 'award':
|
|
16
|
+
cHeight = 130;
|
|
17
|
+
cWidth = 130;
|
|
18
|
+
break;
|
|
19
|
+
case 'fill':
|
|
20
|
+
cHeight = 'auto';
|
|
21
|
+
cWidth = '100%';
|
|
22
|
+
break;
|
|
23
|
+
default:
|
|
24
|
+
cHeight = height;
|
|
25
|
+
cWidth = width;
|
|
26
|
+
break;
|
|
15
27
|
}
|
|
16
28
|
// eslint-disable-next-line @next/next/no-img-element
|
|
17
|
-
return <img src={src} alt={alt} height={cHeight} width={cWidth} />;
|
|
29
|
+
return <img src={src} alt={alt} height={cHeight} width={cWidth} {...rest} />;
|
|
18
30
|
};
|
|
19
31
|
export default NsImage;
|
|
@@ -7,7 +7,7 @@ type Story = StoryObj<ComponentType>;
|
|
|
7
7
|
type MetaStory = Meta<ComponentType>;
|
|
8
8
|
|
|
9
9
|
export default {
|
|
10
|
-
title: 'Components/NsNextImage',
|
|
10
|
+
title: 'Components/Data Display/NsNextImage',
|
|
11
11
|
component: NsNextImage,
|
|
12
12
|
decorators: [withCenterDecorator],
|
|
13
13
|
} as MetaStory;
|
package/src/common/index.ts
CHANGED
|
@@ -13,16 +13,20 @@ export * from './column-control';
|
|
|
13
13
|
export * from './checkbox';
|
|
14
14
|
export * from './common.types.d';
|
|
15
15
|
export * from './dropdown';
|
|
16
|
+
export * from './email-simple';
|
|
16
17
|
export * from './flag';
|
|
18
|
+
export * from './hooks';
|
|
17
19
|
export * from './icon';
|
|
18
20
|
export * from './image';
|
|
19
21
|
export * from './input';
|
|
20
22
|
export * from './label';
|
|
21
23
|
export * from './link';
|
|
24
|
+
export * from './modal';
|
|
22
25
|
export * from './nav-tab';
|
|
23
26
|
export * from './nav-product-card';
|
|
27
|
+
export * from './radiogroup';
|
|
28
|
+
export * from './spinner';
|
|
24
29
|
export * from './text-strip';
|
|
25
30
|
export * from './table';
|
|
26
31
|
export * from './typography';
|
|
27
|
-
export * from './
|
|
28
|
-
export * from './radiogroup';
|
|
32
|
+
export * from './utility';
|
|
@@ -1,20 +1,25 @@
|
|
|
1
1
|
import { Box } from '@mui/material';
|
|
2
|
-
import { styled, nuskinTheme } from '@nuskin/foundation-theme';
|
|
3
|
-
import {
|
|
2
|
+
import { styled, nuskinTheme, ColorUtils, FoundationTheme } from '@nuskin/foundation-theme';
|
|
3
|
+
import {fieldStateType} from './types.d';
|
|
4
4
|
|
|
5
|
+
const { isDarkMode } = ColorUtils;
|
|
5
6
|
const { colors } = nuskinTheme;
|
|
6
7
|
|
|
7
8
|
const options = {
|
|
8
9
|
shouldForwardProp: (propKey: string) => propKey !== 'fieldState',
|
|
9
10
|
};
|
|
10
11
|
|
|
11
|
-
const getInputBorderColor = (
|
|
12
|
+
const getInputBorderColor = (
|
|
13
|
+
palette: FoundationTheme.Theme['palette'],
|
|
14
|
+
fieldState: fieldStateType,
|
|
15
|
+
isLight = true
|
|
16
|
+
): string => {
|
|
12
17
|
if (fieldState === 'success') {
|
|
13
|
-
return
|
|
18
|
+
return colors.success[40];
|
|
14
19
|
} else if (fieldState === 'error') {
|
|
15
20
|
return isLight ? colors.red[40] : colors.red[20];
|
|
16
21
|
} else {
|
|
17
|
-
return palette
|
|
22
|
+
return palette?.primary.main ?? '';
|
|
18
23
|
}
|
|
19
24
|
};
|
|
20
25
|
|
|
@@ -28,7 +33,7 @@ const InputWrapper = styled(
|
|
|
28
33
|
)<InputWrapperType>(({ theme, fieldState }) => {
|
|
29
34
|
/* default value added so that unit tests do not fail */
|
|
30
35
|
const { palette = { primary: { main: '', contrastText: '' } } } = theme;
|
|
31
|
-
const isLight =
|
|
36
|
+
const isLight = isDarkMode({ theme });
|
|
32
37
|
const inputFocusedBorder = isLight ? colors.accentColorB[20] : colors.accentColorB[30];
|
|
33
38
|
const inputDisabledBg = isLight ? colors.neutral[30] : colors.neutral[70];
|
|
34
39
|
const inputDisabledBorder = isLight ? colors.neutral[60] : colors.neutral[80];
|
|
@@ -56,7 +61,7 @@ const InputWrapper = styled(
|
|
|
56
61
|
},
|
|
57
62
|
},
|
|
58
63
|
fieldset: {
|
|
59
|
-
border: `1px solid ${getInputBorderColor(palette
|
|
64
|
+
border: `1px solid ${getInputBorderColor(palette, fieldState, isLight)}`,
|
|
60
65
|
},
|
|
61
66
|
input: {
|
|
62
67
|
color: palette.primary.main,
|
|
@@ -55,7 +55,7 @@ const NsInputField = (props: NsInputFieldProps) => {
|
|
|
55
55
|
} = props;
|
|
56
56
|
/** EndIcon is created so that we can automatically add success and error icon based of fieldState,
|
|
57
57
|
* it will override iconEnd. Also to note, InputProps is given to override both iconStart, iconEnd and EndIcon */
|
|
58
|
-
|
|
58
|
+
const EndIcon: AdornmentPropsType = getAdornmentProps({ disabled, fieldState, iconEnd, iconStart });
|
|
59
59
|
|
|
60
60
|
return (
|
|
61
61
|
<InputFieldWrapper>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render } from '@/test-utils/index';
|
|
3
|
+
import NsModal from './NsModal';
|
|
4
|
+
import { NsModalData } from './stories/mock';
|
|
5
|
+
import { NsModalProps } from './types';
|
|
6
|
+
|
|
7
|
+
describe('NsModal', () => {
|
|
8
|
+
const onHandleClose = jest.fn();
|
|
9
|
+
|
|
10
|
+
const NsModalProp = NsModalData as NsModalProps;
|
|
11
|
+
|
|
12
|
+
test('1. check NsModal component open', () => {
|
|
13
|
+
const { getByTestId } = render(<NsModal {...NsModalProp} handleClose={onHandleClose} />);
|
|
14
|
+
expect(getByTestId('nsmodal-dialog')).toBeInTheDocument();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test('2. should render NsModal default component', () => {
|
|
18
|
+
const { getByTestId } = render(<NsModal {...NsModalProp} handleClose={onHandleClose} />);
|
|
19
|
+
expect(getByTestId('modal-default')).toBeInTheDocument();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test('3. should render NsModal status component', () => {
|
|
23
|
+
const VariantProps: NsModalProps = {
|
|
24
|
+
...NsModalProp,
|
|
25
|
+
variant: 'info',
|
|
26
|
+
};
|
|
27
|
+
const { getByTestId } = render(<NsModal {...VariantProps} handleClose={onHandleClose} />);
|
|
28
|
+
expect(getByTestId('modal-status')).toBeInTheDocument();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('4. should render NsModal title with close button', () => {
|
|
32
|
+
const { getByTestId } = render(<NsModal {...NsModalProp} handleClose={onHandleClose} />);
|
|
33
|
+
expect(getByTestId('modal-title')).toBeInTheDocument();
|
|
34
|
+
expect(getByTestId('modal-close-button')).toBeInTheDocument();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('5. should render NsModal body content', () => {
|
|
38
|
+
const { getByTestId } = render(<NsModal {...NsModalProp} handleClose={onHandleClose} />);
|
|
39
|
+
expect(getByTestId('modal-body-content')).toBeInTheDocument();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('6. should render NsModal footer', () => {
|
|
43
|
+
const { getByTestId } = render(<NsModal {...NsModalProp} handleClose={onHandleClose} />);
|
|
44
|
+
expect(getByTestId('modal-footer')).toBeInTheDocument();
|
|
45
|
+
});
|
|
46
|
+
});
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { styled } from '@nuskin/foundation-theme';
|
|
2
|
+
import { NsButton } from '../button';
|
|
3
|
+
import { DialogActions } from '@mui/material';
|
|
4
|
+
|
|
5
|
+
export const NsInfoWrapper = styled.div`
|
|
6
|
+
display: flex;
|
|
7
|
+
.status-icon {
|
|
8
|
+
padding: 16px 0px 0px 24px;
|
|
9
|
+
}
|
|
10
|
+
.title-icon {
|
|
11
|
+
padding-right: 10px;
|
|
12
|
+
}
|
|
13
|
+
`;
|
|
14
|
+
|
|
15
|
+
export const CloseButton = styled(NsButton)`
|
|
16
|
+
position: absolute;
|
|
17
|
+
right: 8px;
|
|
18
|
+
top: 8px;
|
|
19
|
+
`;
|
|
20
|
+
|
|
21
|
+
export const ModalFooter = styled(DialogActions)`
|
|
22
|
+
display: flex;
|
|
23
|
+
justify-content: space-around;
|
|
24
|
+
padding: 20px 0px 10px 0px;
|
|
25
|
+
button {
|
|
26
|
+
width: 100%;
|
|
27
|
+
}
|
|
28
|
+
`;
|