@nuskin/foundation-ui-components 1.0.2 → 1.1.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 +105 -101
- package/src/common/accordion/NsAccordion.tsx +32 -40
- package/src/common/accordion/NsAccordionHeader.tsx +29 -8
- package/src/common/accordion/NsAccordionItem.tsx +24 -4
- package/src/common/accordion/NsCollapse.tsx +96 -35
- package/src/common/accordion/NsToggleSection.tsx +16 -3
- package/src/common/accordion/ToggleSection.styled.tsx +11 -7
- package/src/common/accordion/colorMap.ts +117 -0
- package/src/common/accordion/enums.ts +18 -0
- package/src/common/accordion/index.ts +1 -0
- package/src/common/accordion/specs/NsAccordion.spec.tsx +101 -31
- package/src/common/accordion/specs/NsAccordionHeader.spec.tsx +77 -10
- package/src/common/accordion/specs/NsAccordionItem.spec.tsx +27 -10
- package/src/common/accordion/specs/NsCollapse.spec.tsx +109 -17
- package/src/common/accordion/specs/NsToggleSection.spec.tsx +50 -14
- package/src/common/accordion/stories/NsAccordion.stories.tsx +32 -14
- package/src/common/accordion/stories/NsAccordionHeader.stories.tsx +6 -2
- package/src/common/accordion/stories/NsAccordionItem.stories.tsx +9 -4
- package/src/common/accordion/stories/NsCollapse.stories.tsx +20 -5
- package/src/common/accordion/stories/NsToggleSection.stories.tsx +7 -4
- package/src/common/accordion/stories/data.tsx +140 -19
- package/src/common/accordion/types.d.ts +55 -47
- 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 +34 -11
- package/src/common/autocomplete/NsAutocomplete.stories.tsx +19 -3
- package/src/common/autocomplete/NsAutocomplete.tsx +63 -24
- package/src/common/autocomplete/mock/MockData.ts +50 -7
- package/src/common/autocomplete/mock/MockUI.tsx +2 -1
- package/src/common/autocomplete/types.d.ts +7 -4
- 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/NsFfgCard.tsx +13 -7
- package/src/common/card/specs/NsFfgCard.spec.tsx +18 -8
- 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 +48 -15
- package/src/common/card/styled/NsFfgStyled.tsx +10 -7
- package/src/common/card/types.d.ts +7 -4
- package/src/common/carousal/NsCarousal.tsx +3 -9
- 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.spec.tsx +5 -16
- package/src/common/cart-icon/NsCartIcon.stories.tsx +2 -2
- package/src/common/cart-icon/NsCartIcon.styled.ts +7 -33
- package/src/common/cart-icon/NsCartIcon.tsx +5 -14
- package/src/common/cart-icon/types.d.ts +1 -0
- 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 +14 -5
- 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.spec.tsx +1 -1
- package/src/common/icon/NsIcon.stories.tsx +4 -1
- package/src/common/icon/NsIcon.tsx +6 -2
- package/src/common/icon/NsIconVariants.ts +4 -1
- package/src/common/icon/icons/AlertErrorIcon.tsx +2 -2
- package/src/common/icon/icons/ArrowLeft.tsx +3 -6
- package/src/common/icon/icons/ArrowRight.tsx +6 -8
- package/src/common/icon/icons/Cart.tsx +7 -32
- package/src/common/icon/icons/CartBadge.tsx +2 -2
- package/src/common/icon/icons/CloseIcon.tsx +1 -1
- package/src/common/icon/icons/ErrorIcon.tsx +2 -2
- package/src/common/icon/icons/HamburgerIcon.tsx +5 -2
- package/src/common/icon/icons/SearchIcon.tsx +1 -1
- package/src/common/icon/icons/SuccessIcon.tsx +2 -7
- package/src/common/icon/icons/TrashIcon.tsx +1 -1
- package/src/common/icon/icons/UserIcon.tsx +3 -6
- package/src/common/icon/icons/UserOutline.tsx +24 -0
- package/src/common/icon/icons/WarningIcon.tsx +1 -1
- package/src/common/icon/icons/home.tsx +1 -1
- 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 +85 -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/NavDrawer.tsx +34 -0
- package/src/common/nav-tab/NavTitles.tsx +52 -0
- package/src/common/nav-tab/NsNavTab.spec.tsx +4 -5
- package/src/common/nav-tab/NsNavTab.stories.tsx +4 -4
- package/src/common/nav-tab/NsNavTab.styled.tsx +13 -7
- package/src/common/nav-tab/NsNavTab.tsx +21 -75
- package/src/common/nav-tab/index.ts +1 -1
- package/src/common/nav-tab/types.d.ts +3 -3
- 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 +20 -19
- 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
|
@@ -1,73 +1,81 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ReactElement, ReactNode } from 'react';
|
|
2
2
|
import { AdditionalProps } from '../common.types';
|
|
3
|
-
import { NsIconNameType } from '../icon';
|
|
3
|
+
import { NsIconNameType, NsIconProps } from '../icon';
|
|
4
4
|
import { Variants } from '../typography';
|
|
5
|
+
import { AccordionContentType, AccordionVariantType } from './enums';
|
|
6
|
+
|
|
7
|
+
export type AccordionItem = {
|
|
8
|
+
allowMultipleExpanded?: boolean;
|
|
9
|
+
type: AccordionContentType;
|
|
10
|
+
id: string;
|
|
11
|
+
content: string | AccordionItem[] | ReactElement;
|
|
12
|
+
icon?: NsIconProps;
|
|
13
|
+
title: string;
|
|
14
|
+
isDefaultOpen?: boolean;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type NsAccordionProps = AdditionalProps & {
|
|
18
|
+
invert: boolean;
|
|
19
|
+
level: number;
|
|
20
|
+
variant: AccordionVariantType;
|
|
21
|
+
items?: AccordionItem[];
|
|
22
|
+
isChild?: boolean;
|
|
23
|
+
headerTypographyVariant?: Variants;
|
|
24
|
+
/** NsIcon's icon name to be shown in opened state */
|
|
25
|
+
openedIconName?: NsIconNameType;
|
|
26
|
+
/** NsIcon's icon name to be shown in closed state */
|
|
27
|
+
closedIconName?: NsIconNameType;
|
|
28
|
+
allowMultipleExpanded: boolean;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
type CommonAccordionType = {
|
|
32
|
+
invert: boolean;
|
|
33
|
+
level: number;
|
|
34
|
+
variant: AccordionVariantType;
|
|
35
|
+
content: AccordionItem['content'];
|
|
36
|
+
};
|
|
5
37
|
|
|
6
38
|
export type NsAccordionHeaderProps = AdditionalProps & {
|
|
7
39
|
title: string | ReactNode;
|
|
8
40
|
// eslint-disable-next-line no-unused-vars
|
|
9
41
|
onClick: (...args: any[]) => void;
|
|
10
42
|
/** a flag for the header, based on which header can change properties */
|
|
11
|
-
isOpen
|
|
43
|
+
isOpen: boolean;
|
|
12
44
|
/** NsIcon's icon name to be shown in opened state */
|
|
13
45
|
openedIconName?: NsIconNameType;
|
|
14
46
|
/** NsIcon's icon name to be shown in closed state */
|
|
15
47
|
closedIconName?: NsIconNameType;
|
|
16
|
-
variant: AccordionVariant;
|
|
17
48
|
headerTypographyVariant?: Variants;
|
|
18
|
-
|
|
49
|
+
startIcon?: NsIconProps;
|
|
50
|
+
} & Pick<CommonAccordionType, 'invert' | 'variant'>;
|
|
19
51
|
|
|
20
|
-
export type NsCollapseProps = AdditionalProps &
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
export type NsFramerCollapseProps = NsCollapseProps & {
|
|
27
|
-
transitionDetails?: {
|
|
28
|
-
duration?: number;
|
|
29
|
-
ease?: number[];
|
|
30
|
-
};
|
|
31
|
-
};
|
|
52
|
+
export type NsCollapseProps = AdditionalProps & {
|
|
53
|
+
isOpen: boolean;
|
|
54
|
+
isChild: boolean;
|
|
55
|
+
allowMultipleExpanded?: boolean;
|
|
56
|
+
type: AccordionContentType;
|
|
57
|
+
} & CommonAccordionType;
|
|
32
58
|
|
|
33
|
-
export type NsToggleSectionProps = AdditionalProps &
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
59
|
+
export type NsToggleSectionProps = AdditionalProps & {
|
|
60
|
+
title: string | ReactNode;
|
|
61
|
+
/** if true, NsToggle is rendered in open state, that collapsible section is visible */
|
|
62
|
+
defaultOpen?: boolean;
|
|
63
|
+
isChild: boolean;
|
|
64
|
+
type: AccordionContentType;
|
|
65
|
+
} & Pick<CommonAccordionType, 'invert' | 'variant' | 'content'>;
|
|
40
66
|
|
|
41
67
|
export type NsAccordionItemProps = AdditionalProps & {
|
|
42
68
|
isOpen: boolean;
|
|
43
69
|
// eslint-disable-next-line no-unused-vars
|
|
44
70
|
toggleOpen(index: number, isOpen: boolean): void;
|
|
45
71
|
title: string | ReactNode;
|
|
46
|
-
content: ReactNode;
|
|
47
|
-
variant: AccordionVariant;
|
|
48
72
|
headerTypographyVariant?: Variants;
|
|
49
73
|
/** NsIcon's icon name to be shown in opened state */
|
|
50
74
|
openedIconName?: NsIconNameType;
|
|
51
75
|
/** NsIcon's icon name to be shown in closed state */
|
|
52
76
|
closedIconName?: NsIconNameType;
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
body: ReactNode;
|
|
59
|
-
isDefaultOpen?: boolean;
|
|
60
|
-
}[];
|
|
61
|
-
variant: AccordionVariant;
|
|
62
|
-
headerTypographyVariant?: Variants;
|
|
63
|
-
/** NsIcon's icon name to be shown in opened state */
|
|
64
|
-
openedIconName?: NsIconNameType;
|
|
65
|
-
/** NsIcon's icon name to be shown in closed state */
|
|
66
|
-
closedIconName?: NsIconNameType;
|
|
67
|
-
};
|
|
68
|
-
export enum AccordionVariant {
|
|
69
|
-
// eslint-disable-next-line no-unused-vars
|
|
70
|
-
Basic = 'basic', // basic is as per figma
|
|
71
|
-
// eslint-disable-next-line no-unused-vars
|
|
72
|
-
Compact = 'compact', // compact is as per test.nuskin.com
|
|
73
|
-
}
|
|
77
|
+
startIcon?: NsIconProps;
|
|
78
|
+
isChild: boolean;
|
|
79
|
+
type: AccordionContentType;
|
|
80
|
+
allowMultipleExpanded?: boolean;
|
|
81
|
+
} & CommonAccordionType;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { FoundationTheme } from '@nuskin/foundation-theme';
|
|
3
|
+
|
|
4
|
+
import type { HeaderProps } from './types.d';
|
|
5
|
+
import { NsIconNameType } from '../icon/types';
|
|
6
|
+
|
|
7
|
+
export const colorMap: { [key: string]: FoundationTheme.PaletteColors } = {
|
|
8
|
+
error: 'error',
|
|
9
|
+
notice: 'info',
|
|
10
|
+
success: 'secondary',
|
|
11
|
+
warning: 'warning',
|
|
12
|
+
default: 'info',
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const iconMap: { [key: string]: NsIconNameType } = {
|
|
16
|
+
error: 'alertError',
|
|
17
|
+
success: 'success',
|
|
18
|
+
warning: 'warning',
|
|
19
|
+
default: 'infoFilled',
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const Header: React.FC<HeaderProps> = ({ children, head }) => (
|
|
23
|
+
<>
|
|
24
|
+
{children}
|
|
25
|
+
{head}
|
|
26
|
+
</>
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
export default Header;
|
|
@@ -6,12 +6,12 @@ const addEventListenerMock = jest.spyOn(window, 'addEventListener');
|
|
|
6
6
|
const removeEventListenerMock = jest.spyOn(window, 'removeEventListener');
|
|
7
7
|
|
|
8
8
|
describe('NsAlert', () => {
|
|
9
|
-
test('renders alert with provided props', () => {
|
|
9
|
+
test('1. renders alert with provided props', () => {
|
|
10
10
|
render(
|
|
11
11
|
<NsAlert
|
|
12
12
|
context="error"
|
|
13
13
|
title="Error Alert"
|
|
14
|
-
|
|
14
|
+
threshold={800}
|
|
15
15
|
type="banner"
|
|
16
16
|
visible
|
|
17
17
|
head={<span>Alert Head</span>}
|
|
@@ -27,13 +27,13 @@ describe('NsAlert', () => {
|
|
|
27
27
|
expect(screen.getByText('Dismiss')).toBeInTheDocument();
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
-
test('calls onDismiss function when dismissed', () => {
|
|
30
|
+
test('2. calls onDismiss function when dismissed', () => {
|
|
31
31
|
const onDismissMock = jest.fn();
|
|
32
32
|
render(
|
|
33
33
|
<NsAlert
|
|
34
34
|
context="info"
|
|
35
35
|
title="Info Alert"
|
|
36
|
-
|
|
36
|
+
threshold={600}
|
|
37
37
|
type="auto"
|
|
38
38
|
visible
|
|
39
39
|
head={<span>Alert Head</span>}
|
|
@@ -48,12 +48,12 @@ describe('NsAlert', () => {
|
|
|
48
48
|
expect(onDismissMock).toHaveBeenCalled();
|
|
49
49
|
});
|
|
50
50
|
|
|
51
|
-
test('hides alert when not visible', () => {
|
|
51
|
+
test('3. hides alert when not visible', () => {
|
|
52
52
|
render(
|
|
53
53
|
<NsAlert
|
|
54
54
|
context="success"
|
|
55
55
|
title="Success Alert"
|
|
56
|
-
|
|
56
|
+
threshold={700}
|
|
57
57
|
type="card"
|
|
58
58
|
visible={false}
|
|
59
59
|
head={<span>Alert Head</span>}
|
|
@@ -64,13 +64,13 @@ describe('NsAlert', () => {
|
|
|
64
64
|
expect(screen.getByTestId('alert-container')).toHaveStyle('display: none');
|
|
65
65
|
});
|
|
66
66
|
|
|
67
|
-
test('adds and removes event listener on mount/unmount', () => {
|
|
67
|
+
test('4. adds and removes event listener on mount/unmount', () => {
|
|
68
68
|
act(() => {
|
|
69
69
|
render(
|
|
70
70
|
<NsAlert
|
|
71
71
|
context="notice"
|
|
72
72
|
title="Success Alert"
|
|
73
|
-
|
|
73
|
+
threshold={700}
|
|
74
74
|
type="card"
|
|
75
75
|
visible={false}
|
|
76
76
|
head={<span>Alert Head</span>}
|
|
@@ -84,7 +84,22 @@ describe('NsAlert', () => {
|
|
|
84
84
|
expect(removeEventListenerMock).toHaveBeenCalledWith('resize', expect.any(Function));
|
|
85
85
|
});
|
|
86
86
|
|
|
87
|
-
test('updates windowWidth on resize', () => {
|
|
87
|
+
test('5. updates windowWidth on resize and auto type and greater threshold to select card', () => {
|
|
88
|
+
const originalInnerWidth = window.innerWidth;
|
|
89
|
+
window.innerWidth = 500; // Set a desired window width for testing
|
|
90
|
+
|
|
91
|
+
act(() => {
|
|
92
|
+
render(<NsAlert threshold={2000} type="auto" />);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Simulate a resize event by restoring the original innerWidth
|
|
96
|
+
window.innerWidth = originalInnerWidth;
|
|
97
|
+
|
|
98
|
+
act(() => {
|
|
99
|
+
window.dispatchEvent(new Event('resize'));
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
test('6. updates windowWidth on resize', () => {
|
|
88
103
|
const originalInnerWidth = window.innerWidth;
|
|
89
104
|
window.innerWidth = 500; // Set a desired window width for testing
|
|
90
105
|
|
|
@@ -100,12 +115,12 @@ describe('NsAlert', () => {
|
|
|
100
115
|
});
|
|
101
116
|
});
|
|
102
117
|
|
|
103
|
-
test('get props error in console for types', () => {
|
|
118
|
+
test('7. get props error in console for types', () => {
|
|
104
119
|
render(
|
|
105
120
|
<NsAlert
|
|
106
121
|
context="warning"
|
|
107
122
|
title="warning Alert"
|
|
108
|
-
|
|
123
|
+
threshold={800}
|
|
109
124
|
type="noCard"
|
|
110
125
|
visible
|
|
111
126
|
head={<span>Alert Head</span>}
|
|
@@ -116,12 +131,12 @@ describe('NsAlert', () => {
|
|
|
116
131
|
expect(screen.getByTestId('alert-container')).toHaveStyle('display: block');
|
|
117
132
|
});
|
|
118
133
|
|
|
119
|
-
test('get props error in console for context', () => {
|
|
134
|
+
test('8. get props error in console for context', () => {
|
|
120
135
|
render(
|
|
121
136
|
<NsAlert
|
|
122
137
|
context="warningError"
|
|
123
138
|
title="warning Alert"
|
|
124
|
-
|
|
139
|
+
threshold={800}
|
|
125
140
|
type="card"
|
|
126
141
|
visible
|
|
127
142
|
head={<span>Alert Head</span>}
|
|
@@ -131,4 +146,22 @@ describe('NsAlert', () => {
|
|
|
131
146
|
|
|
132
147
|
expect(screen.getByTestId('alert-container')).toHaveStyle('display: block');
|
|
133
148
|
});
|
|
149
|
+
test('9. console.error to be called twice', () => {
|
|
150
|
+
const original = console.error;
|
|
151
|
+
console.error = jest.fn();
|
|
152
|
+
render(
|
|
153
|
+
<NsAlert
|
|
154
|
+
context=""
|
|
155
|
+
title="warning Alert"
|
|
156
|
+
threshold={2000}
|
|
157
|
+
type=""
|
|
158
|
+
visible
|
|
159
|
+
head={<span>Alert Head</span>}
|
|
160
|
+
body={<p>Alert Body</p>}
|
|
161
|
+
/>
|
|
162
|
+
);
|
|
163
|
+
expect(console.error).toHaveBeenCalledTimes(2);
|
|
164
|
+
console.error = original;
|
|
165
|
+
expect(screen.getByTestId('alert-container')).toHaveStyle('display: block');
|
|
166
|
+
});
|
|
134
167
|
});
|
|
@@ -8,7 +8,7 @@ type Story = StoryObj<ComponentType>;
|
|
|
8
8
|
type MetaStory = Meta<ComponentType>;
|
|
9
9
|
|
|
10
10
|
export default {
|
|
11
|
-
title: 'Components/NsAlert',
|
|
11
|
+
title: 'Components/Feedback/NsAlert',
|
|
12
12
|
component: NsAlert,
|
|
13
13
|
decorators: [withCenterDecorator],
|
|
14
14
|
argTypes: {
|
|
@@ -24,7 +24,7 @@ export default {
|
|
|
24
24
|
args: {
|
|
25
25
|
context: 'info',
|
|
26
26
|
title: 'Information',
|
|
27
|
-
|
|
27
|
+
threshold: 600,
|
|
28
28
|
type: 'banner',
|
|
29
29
|
visible: true,
|
|
30
30
|
onDismiss: () => {},
|
|
@@ -40,4 +40,4 @@ export default {
|
|
|
40
40
|
},
|
|
41
41
|
} as MetaStory;
|
|
42
42
|
|
|
43
|
-
export const
|
|
43
|
+
export const Alert: Story = {};
|
|
@@ -3,27 +3,27 @@ import { styled, css, ColorUtils, FoundationTheme } from '@nuskin/foundation-the
|
|
|
3
3
|
|
|
4
4
|
const { color } = ColorUtils;
|
|
5
5
|
|
|
6
|
-
import type { AlertContainerParams, AlertType, AlertParams, TypeMapCSS } from './types
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
6
|
+
import type { AlertContainerParams, AlertType, AlertParams, TypeMapCSS } from './types';
|
|
7
|
+
const gutterWidth = '10px';
|
|
8
|
+
const closeButtonWidth = '12px';
|
|
9
|
+
const iconWidth = '24px';
|
|
10
10
|
|
|
11
11
|
const TypeMap: TypeMapCSS = {
|
|
12
12
|
banner: {
|
|
13
|
-
|
|
13
|
+
minHeight: '114px',
|
|
14
14
|
padding: '15px 45px',
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
contentColumnStart: 'row-center',
|
|
16
|
+
contentColumnEnd: 'row-center',
|
|
17
|
+
barPosition: 'top',
|
|
18
|
+
footVisibility: 'block',
|
|
19
19
|
},
|
|
20
20
|
card: {
|
|
21
|
-
|
|
21
|
+
minHeight: '72px',
|
|
22
22
|
padding: '24px 24px',
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
contentColumnStart: 'row-left',
|
|
24
|
+
contentColumnEnd: 'row-right',
|
|
25
|
+
barPosition: 'left',
|
|
26
|
+
footDisplay: 'none',
|
|
27
27
|
},
|
|
28
28
|
};
|
|
29
29
|
|
|
@@ -53,14 +53,14 @@ const fontFamilyStyles = css`
|
|
|
53
53
|
export const Alert = styled.div<AlertParams>`
|
|
54
54
|
position: relative;
|
|
55
55
|
display: grid;
|
|
56
|
-
grid-template-columns: [row-left] ${
|
|
56
|
+
grid-template-columns: [row-left] ${iconWidth} [space] ${gutterWidth} [row-center] auto [space] ${gutterWidth} [row-right] ${closeButtonWidth};
|
|
57
57
|
grid-template-rows: [row1] auto;
|
|
58
58
|
grid-auto-row: auto;
|
|
59
59
|
grid-auto-flow: row;
|
|
60
60
|
background-color: ${color('primary', 'white')};
|
|
61
61
|
border: 1px solid ${color('primary', 'black')};
|
|
62
|
-
min-height: ${({ type }) => TypeMap?.[type]?.
|
|
63
|
-
padding: ${({ type }) => TypeMap?.[type]?.
|
|
62
|
+
min-height: ${({ type }) => TypeMap?.[type]?.minHeight};
|
|
63
|
+
padding: ${({ type }) => TypeMap?.[type]?.padding};
|
|
64
64
|
color: ${(props) => color(contextType?.[props?.context] as FoundationTheme.AppColors, '40')(props)} };
|
|
65
65
|
`;
|
|
66
66
|
|
|
@@ -78,8 +78,8 @@ export const AlertIcon = styled.div`
|
|
|
78
78
|
grid-column-end: row-left;
|
|
79
79
|
grid-row-start: row1;
|
|
80
80
|
grid-row-end: row1;
|
|
81
|
-
width: ${
|
|
82
|
-
height: ${
|
|
81
|
+
width: ${iconWidth};
|
|
82
|
+
height: ${iconWidth};
|
|
83
83
|
`;
|
|
84
84
|
|
|
85
85
|
export const AlertHead = styled.div`
|
|
@@ -102,14 +102,14 @@ export const AlertHead = styled.div`
|
|
|
102
102
|
export const AlertContent = styled.div<{ type: AlertType }>`
|
|
103
103
|
margin-top: 8px;
|
|
104
104
|
color: ${color('primary', 'black')};
|
|
105
|
-
grid-column-start: ${({ type }) => TypeMap?.[type]?.
|
|
106
|
-
grid-column-end: ${({ type }) => TypeMap?.[type]?.
|
|
105
|
+
grid-column-start: ${({ type }) => TypeMap?.[type]?.contentColumnStart};
|
|
106
|
+
grid-column-end: ${({ type }) => TypeMap?.[type]?.contentColumnEnd};
|
|
107
107
|
`;
|
|
108
108
|
|
|
109
109
|
export const AlertFoot = styled.div<{ type: AlertType }>`
|
|
110
110
|
margin-top: 8px;
|
|
111
111
|
color: ${color('primary', 'black')};
|
|
112
|
-
display: ${({ type }) => TypeMap?.[type]?.['
|
|
112
|
+
display: ${({ type }) => TypeMap?.[type]?.['footDisplay']};
|
|
113
113
|
${fontFamilyStyles}
|
|
114
114
|
font-weight: 300;
|
|
115
115
|
font-size: 14px;
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import React, { useEffect, useState } from 'react';
|
|
2
|
-
import { FoundationTheme } from '@nuskin/foundation-theme';
|
|
3
|
-
|
|
4
2
|
import {
|
|
5
3
|
AlertContainer,
|
|
6
4
|
Alert,
|
|
@@ -11,44 +9,15 @@ import {
|
|
|
11
9
|
AlertFoot,
|
|
12
10
|
AlertBody,
|
|
13
11
|
} from './NsAlert.styled';
|
|
14
|
-
import type { NsAlertProps,
|
|
12
|
+
import type { NsAlertProps, ContextType, AlertType } from './types.d';
|
|
15
13
|
import { NsIcon } from '../icon';
|
|
16
|
-
import {
|
|
17
|
-
|
|
18
|
-
const Header: React.FC<HeaderProps> = ({ children, head }) => (
|
|
19
|
-
<>
|
|
20
|
-
{children}
|
|
21
|
-
{head}
|
|
22
|
-
</>
|
|
23
|
-
);
|
|
24
|
-
|
|
25
|
-
function isValidContextValue(value: string): boolean {
|
|
26
|
-
return (value as ContextType) !== undefined;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function isValidTypeValue(value: string): boolean {
|
|
30
|
-
return (value as AlertType) !== undefined;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const colorMap: { [key: string]: FoundationTheme.PaletteColors } = {
|
|
34
|
-
error: 'error',
|
|
35
|
-
notice: 'info',
|
|
36
|
-
success: 'secondary',
|
|
37
|
-
warning: 'warning',
|
|
38
|
-
default: 'info',
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
const iconMap: { [key: string]: NsIconNameType } = {
|
|
42
|
-
error: 'alertError',
|
|
43
|
-
success: 'success',
|
|
44
|
-
warning: 'warning',
|
|
45
|
-
default: 'infoFilled',
|
|
46
|
-
};
|
|
14
|
+
import Header, { colorMap, iconMap } from './Header';
|
|
15
|
+
import useWindowSize from '../hooks/useWindowSize';
|
|
47
16
|
|
|
48
17
|
export default function NsAlert({
|
|
49
18
|
context = 'info',
|
|
50
19
|
title = 'Information',
|
|
51
|
-
|
|
20
|
+
threshold = 600,
|
|
52
21
|
type = 'banner',
|
|
53
22
|
visible = false,
|
|
54
23
|
head,
|
|
@@ -57,76 +26,42 @@ export default function NsAlert({
|
|
|
57
26
|
onDismiss = undefined,
|
|
58
27
|
}: NsAlertProps) {
|
|
59
28
|
const [alertType, setAlertType] = useState('banner');
|
|
60
|
-
const
|
|
29
|
+
const windowSize = useWindowSize();
|
|
61
30
|
|
|
62
31
|
const hasDismissListener = typeof onDismiss === 'function';
|
|
63
32
|
|
|
64
|
-
const getColor = () => {
|
|
65
|
-
return colorMap[context] || colorMap.default;
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
const getIcon = () => {
|
|
69
|
-
return iconMap[context] || iconMap.default;
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
const getTitleText = () => {
|
|
73
|
-
if (title) {
|
|
74
|
-
return title;
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
const handleDismiss = () => {
|
|
79
|
-
if (hasDismissListener) {
|
|
80
|
-
onDismiss();
|
|
81
|
-
}
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
const handleResize = () => {
|
|
85
|
-
setWindowWidth(window.innerWidth);
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
useEffect(() => {
|
|
89
|
-
let aType = '';
|
|
90
|
-
if (type === 'auto') {
|
|
91
|
-
aType = windowWidth < threshhold ? 'card' : 'banner';
|
|
92
|
-
} else {
|
|
93
|
-
aType = type;
|
|
94
|
-
}
|
|
95
|
-
setAlertType(aType);
|
|
96
|
-
}, [threshhold, type, windowWidth]);
|
|
97
|
-
|
|
98
33
|
useEffect(() => {
|
|
99
|
-
if (!
|
|
34
|
+
if (!context) {
|
|
100
35
|
console.error(`Invalid prop value for 'context': ${context}`);
|
|
101
36
|
}
|
|
102
|
-
|
|
103
|
-
if (!isValidTypeValue(type)) {
|
|
37
|
+
if (!type) {
|
|
104
38
|
console.error(`Invalid prop value for 'type': ${type}`);
|
|
105
39
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
window.removeEventListener('resize', handleResize);
|
|
113
|
-
};
|
|
114
|
-
}, []);
|
|
40
|
+
if (type === 'auto') {
|
|
41
|
+
setAlertType(windowSize.width < threshold ? 'card' : 'banner');
|
|
42
|
+
} else {
|
|
43
|
+
setAlertType(type);
|
|
44
|
+
}
|
|
45
|
+
}, [threshold, type, windowSize.width, context]);
|
|
115
46
|
|
|
116
47
|
return (
|
|
117
48
|
<AlertContainer visible={visible} data-testid="alert-container">
|
|
118
49
|
<Alert type={alertType as AlertType} context={context as ContextType}>
|
|
119
50
|
<AlertIcon>
|
|
120
|
-
<NsIcon
|
|
51
|
+
<NsIcon
|
|
52
|
+
name={iconMap[context] || iconMap.default}
|
|
53
|
+
color={colorMap[context] || colorMap.default}
|
|
54
|
+
size="large"
|
|
55
|
+
/>
|
|
121
56
|
</AlertIcon>
|
|
122
57
|
<AlertHead>
|
|
123
|
-
<Header head={<span>{
|
|
58
|
+
<Header head={<span>{title}</span>}>{head}</Header>
|
|
124
59
|
</AlertHead>
|
|
125
|
-
{hasDismissListener
|
|
126
|
-
<AlertCloseButton data-testid="alert-close-button" onClick={
|
|
60
|
+
{hasDismissListener && (
|
|
61
|
+
<AlertCloseButton data-testid="alert-close-button" onClick={onDismiss}>
|
|
127
62
|
<NsIcon name="close" size="small" />
|
|
128
63
|
</AlertCloseButton>
|
|
129
|
-
)
|
|
64
|
+
)}
|
|
130
65
|
<AlertContent type={alertType as AlertType}>
|
|
131
66
|
<AlertBody>{body}</AlertBody>
|
|
132
67
|
{foot ? <AlertFoot type={alertType as AlertType}>{foot}</AlertFoot> : null}
|
|
@@ -3,7 +3,7 @@ import { ReactNode, CSSProperties } from 'react';
|
|
|
3
3
|
export interface NsAlertProps {
|
|
4
4
|
context?: string;
|
|
5
5
|
title?: string;
|
|
6
|
-
|
|
6
|
+
threshold?: number;
|
|
7
7
|
type?: string;
|
|
8
8
|
visible?: boolean;
|
|
9
9
|
head?: ReactNode;
|
|
@@ -30,12 +30,12 @@ export type AlertParams = {
|
|
|
30
30
|
};
|
|
31
31
|
|
|
32
32
|
export type CustomCSSProperties = CSSProperties & {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
33
|
+
minHeight?: string;
|
|
34
|
+
contentColumnStart?: string;
|
|
35
|
+
contentColumnEnd?: string;
|
|
36
|
+
barPosition?: string;
|
|
37
|
+
footVisibility?: string;
|
|
38
|
+
footDisplay?: string;
|
|
39
39
|
};
|
|
40
40
|
|
|
41
41
|
export interface TypeMapCSS {
|
|
@@ -10,27 +10,45 @@ describe('NsAuthDropdown', () => {
|
|
|
10
10
|
signUpLinkText: 'Sign Up',
|
|
11
11
|
signUpLinkPrefixText: 'Do not have an account?',
|
|
12
12
|
titleText: 'Welcome',
|
|
13
|
-
closeOnMouseLeave: false,
|
|
14
13
|
};
|
|
15
14
|
|
|
16
|
-
it('should render component with title text', () => {
|
|
15
|
+
it('1. should render component with title text', () => {
|
|
17
16
|
render(<NsAuthDropdown {...props} />);
|
|
18
17
|
expect(screen.getByText('Welcome')).toBeInTheDocument();
|
|
19
18
|
});
|
|
20
19
|
|
|
21
|
-
it('should render the sign in button with text', () => {
|
|
20
|
+
it('2. should render the sign in button with text', () => {
|
|
22
21
|
render(<NsAuthDropdown {...props} />);
|
|
23
22
|
const authHead = screen.getByTestId('auth-head');
|
|
24
23
|
fireEvent.click(authHead);
|
|
25
24
|
expect(screen.getByTestId('sign-in-btn')).toBeInTheDocument();
|
|
26
|
-
expect(screen.getByTestId('sign-in-btn')).toHaveTextContent(
|
|
25
|
+
expect(screen.getByTestId('sign-in-btn')).toHaveTextContent(props.signInBtnText);
|
|
26
|
+
expect(screen.getByTestId('signup-link')).toHaveTextContent(props.signUpLinkText);
|
|
27
27
|
});
|
|
28
28
|
|
|
29
|
-
it('should have the correct href attribute in SignUpLink', () => {
|
|
29
|
+
it('3. should have the correct href attribute in SignUpLink', () => {
|
|
30
30
|
render(<NsAuthDropdown {...props} />);
|
|
31
31
|
const authHead = screen.getByTestId('auth-head');
|
|
32
32
|
fireEvent.click(authHead);
|
|
33
33
|
const signUpLink = screen.getByTestId('signup-link');
|
|
34
34
|
expect(signUpLink).toHaveAttribute('href', '/signUp');
|
|
35
35
|
});
|
|
36
|
+
it('4. should close dropdown on mouse leave', () => {
|
|
37
|
+
const { container } = render(<NsAuthDropdown {...props} closeOnMouseLeave={true} />);
|
|
38
|
+
const authHead = screen.getByTestId('auth-head');
|
|
39
|
+
fireEvent.click(authHead);
|
|
40
|
+
expect(screen.getByTestId('sign-in-btn')).toHaveTextContent('Sign In');
|
|
41
|
+
const wrapper = container.querySelector('.auth-wrapper');
|
|
42
|
+
if (wrapper) fireEvent.mouseLeave(wrapper);
|
|
43
|
+
expect(container.querySelector('.auth-wrapper')).toBeNull();
|
|
44
|
+
});
|
|
45
|
+
it('5. should not close dropdown on mouse leave', () => {
|
|
46
|
+
const { container } = render(<NsAuthDropdown {...props} closeOnMouseLeave={false} />);
|
|
47
|
+
const authHead = screen.getByTestId('auth-head');
|
|
48
|
+
fireEvent.click(authHead);
|
|
49
|
+
expect(screen.getByTestId('sign-in-btn')).toHaveTextContent('Sign In');
|
|
50
|
+
const wrapper = container.querySelector('.auth-wrapper');
|
|
51
|
+
if (wrapper) fireEvent.mouseLeave(wrapper);
|
|
52
|
+
expect(container.querySelector('.auth-wrapper')).not.toBeNull();
|
|
53
|
+
});
|
|
36
54
|
});
|
|
@@ -7,7 +7,7 @@ type Story = StoryObj<ComponentType>;
|
|
|
7
7
|
type MetaStory = Meta<ComponentType>;
|
|
8
8
|
|
|
9
9
|
export default {
|
|
10
|
-
title: 'Components/NsAuthDropdown',
|
|
10
|
+
title: 'Components/Data Display/NsAuthDropdown',
|
|
11
11
|
component: NsAuthDropdown,
|
|
12
12
|
decorators: [withCenterDecorator],
|
|
13
13
|
argTypes: {},
|