@nuskin/foundation-ui-components 1.0.0-cft-35.11 → 1.0.0-cft-35.13
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 +21 -16
- package/src/common/alert/NsAlert.spec.tsx +134 -0
- package/src/common/alert/NsAlert.stories.tsx +42 -0
- package/src/common/alert/NsAlert.styled.ts +124 -0
- package/src/common/alert/NsAlert.tsx +155 -0
- package/src/common/alert/index.ts +2 -0
- package/src/common/alert/types.d.ts +45 -0
- package/src/common/auth-dropdown/NsAuthDropdown.stories.tsx +9 -5
- package/src/common/auth-dropdown/NsAuthDropdown.tsx +1 -1
- package/src/common/autocomplete/NsAutocomplete.spec.tsx +96 -0
- package/src/common/autocomplete/NsAutocomplete.stories.tsx +51 -0
- package/src/common/autocomplete/NsAutocomplete.tsx +110 -0
- package/src/common/autocomplete/index.ts +1 -0
- package/src/common/autocomplete/mock/Mock.styled.tsx +23 -0
- package/src/common/autocomplete/mock/MockData.ts +46 -0
- package/src/common/autocomplete/mock/MockUI.tsx +24 -0
- package/src/common/autocomplete/types.ts +39 -0
- package/src/common/banner/NsBanner.spec.tsx +2 -2
- package/src/common/banner/NsBanner.stories.tsx +2 -2
- package/src/common/banner/NsBanner.styled.tsx +1 -0
- package/src/common/banner/NsBanner.tsx +12 -3
- package/src/common/brand-components/NsBrand.stories.tsx +2 -2
- package/src/common/breadcrumb/NsBreadcrumb.spec.tsx +12 -4
- package/src/common/breadcrumb/NsBreadcrumb.stories.tsx +19 -8
- package/src/common/breadcrumb/NsBreadcrumb.styled.tsx +1 -1
- package/src/common/breadcrumb/NsBreadcrumb.tsx +2 -4
- package/src/common/button/NsButton.stories.tsx +2 -2
- package/src/common/card/NsFfgCard.tsx +18 -0
- package/src/common/card/index.ts +1 -0
- package/src/common/card/stories/NsBgCard.stories.tsx +2 -2
- package/src/common/card/stories/NsBrandProductCard.stories.tsx +2 -2
- package/src/common/card/stories/NsFfgCard.stories.tsx +40 -0
- package/src/common/card/styled/NsFfgStyled.tsx +44 -0
- package/src/common/card/tests/NsFfgCard.spec.tsx +23 -0
- package/src/common/card/types.d.ts +9 -0
- package/src/common/carousal/CarousalControl.tsx +3 -5
- package/src/common/carousal/IconWrapper.tsx +4 -4
- package/src/common/carousal/tests/NsCarousal.spec.tsx +4 -4
- package/src/common/cart-icon/NsCartIcon.spec.tsx +10 -4
- package/src/common/cart-icon/NsCartIcon.stories.tsx +2 -2
- package/src/common/checkbox/NsCheckbox.stories.tsx +12 -16
- package/src/common/column-control/NDivs.story.tsx +35 -0
- package/src/common/column-control/NsColumnControl.spec.tsx +24 -0
- package/src/common/column-control/NsColumnControl.stories.tsx +69 -0
- package/src/common/column-control/NsColumnControl.tsx +59 -0
- package/src/common/column-control/index.ts +2 -0
- package/src/common/column-control/types.d.ts +47 -0
- package/src/common/column-control/utils.ts +66 -0
- package/src/common/dropdown/NsDropdown.styled.tsx +5 -1
- package/src/common/dropdown/NsDropdown.tsx +1 -1
- package/src/common/dropdown/stories/NsDropdown.stories.tsx +13 -9
- package/src/common/dropdown/stories/NsDropdownCountry.stories.tsx +13 -9
- package/src/common/flag/NsFlag.stories.tsx +2 -2
- package/src/common/icon/NsIcon.spec.tsx +9 -2
- package/src/common/icon/NsIcon.stories.tsx +4 -17
- package/src/common/icon/NsIcon.tsx +1 -1
- package/src/common/icon/{IconMap.ts → NsIconVariants.ts} +10 -0
- package/src/common/icon/icons/AlertErrorIcon.tsx +36 -0
- package/src/common/icon/icons/ArrowDown.tsx +1 -1
- package/src/common/{carousal/LeftChevron.tsx → icon/icons/ArrowLeft.tsx} +7 -5
- package/src/common/{carousal/RightChevron.tsx → icon/icons/ArrowRight.tsx} +3 -3
- package/src/common/icon/icons/ArrowUp.tsx +1 -1
- package/src/common/icon/icons/Cart.tsx +7 -1
- package/src/common/icon/icons/CartBadge.tsx +7 -1
- package/src/common/icon/icons/CheckShield.tsx +7 -1
- package/src/common/icon/icons/CheckVerified.tsx +7 -1
- package/src/common/icon/icons/CloseIcon.tsx +22 -0
- package/src/common/icon/icons/ErrorIcon.tsx +1 -1
- package/src/common/icon/icons/InfoFilled.tsx +1 -1
- package/src/common/icon/icons/InfoOutlined.tsx +7 -1
- package/src/common/icon/icons/SearchIcon.tsx +1 -1
- package/src/common/icon/icons/SlashIcon.tsx +4 -4
- package/src/common/icon/icons/SuccessIcon.tsx +1 -1
- package/src/common/icon/icons/TrashIcon.tsx +1 -2
- package/src/common/icon/icons/WarningIcon.tsx +36 -0
- package/src/common/icon/icons/home.tsx +4 -3
- package/src/common/icon/index.ts +1 -0
- package/src/common/icon/types.d.ts +2 -15
- package/src/common/image/NsImage.stories.tsx +2 -2
- package/src/common/image/NsImage.tsx +3 -2
- package/src/common/image/NsNextImage.stories.tsx +2 -2
- package/src/common/image/NsNextImage.tsx +3 -7
- package/src/common/image/types.d.ts +6 -4
- package/src/common/index.ts +15 -8
- package/src/common/input/InputFieldWrapper.tsx +2 -3
- package/src/common/input/InputWrapper.tsx +3 -2
- package/src/common/input/NsInputField.spec.tsx +17 -2
- package/src/common/link/NsLink.spec.tsx +15 -8
- package/src/common/link/NsLink.stories.tsx +10 -2
- package/src/common/link/NsLink.styled.tsx +1 -1
- package/src/common/link/NsLink.tsx +26 -4
- package/src/common/link/types.d.ts +4 -2
- package/src/common/nav-product-card/NsNavProductCard.stories.tsx +2 -2
- package/src/common/nav-product-card/NsNavProductCard.tsx +3 -2
- package/src/common/nav-tab/NsNavTab.stories.tsx +2 -2
- package/src/common/typography/NsTypography.stories.tsx +2 -2
- package/src/common/videos/NsVideo.spec.tsx +26 -0
- package/src/common/videos/NsVideo.stories.tsx +32 -0
- package/src/common/videos/NsVideo.tsx +25 -0
- package/src/common/videos/index.ts +2 -0
- package/src/common/videos/types.d.ts +9 -0
- package/CHANGELOG.md +0 -187
- /package/src/common/breadcrumb/{types.ts → types.d.ts} +0 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nuskin/foundation-ui-components",
|
|
3
|
-
"version": "1.0.0-cft-35.
|
|
4
|
-
"description": "It is a foundation library written in react, that provides
|
|
3
|
+
"version": "1.0.0-cft-35.13",
|
|
4
|
+
"description": "It is a foundation library written in react, that provides common components to all UI pages",
|
|
5
5
|
"private": false,
|
|
6
6
|
"main": "src/common/index.ts",
|
|
7
7
|
"repository": {
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
},
|
|
11
11
|
"homepage": "https://code.tls.nuskin.io/ns-am/content-foundation/foundation-ui-components/blob/master/README.md",
|
|
12
12
|
"author": {
|
|
13
|
-
"
|
|
14
|
-
"
|
|
13
|
+
"name": "Ruthraj Palanisamy",
|
|
14
|
+
"email": "rpalanisamy@nuskin.com"
|
|
15
15
|
},
|
|
16
16
|
"publishConfig": {
|
|
17
17
|
"access": "public"
|
|
@@ -27,11 +27,11 @@
|
|
|
27
27
|
"build": "next build",
|
|
28
28
|
"start": "next start",
|
|
29
29
|
"lint": "next lint",
|
|
30
|
-
"storybook": "start-storybook -p 6007",
|
|
31
30
|
"test": "jest",
|
|
32
31
|
"test:coverage": "yarn test --coverage",
|
|
33
32
|
"test:watch": "jest --watch",
|
|
34
|
-
"
|
|
33
|
+
"storybook": "storybook dev -p 6007",
|
|
34
|
+
"build-storybook": "storybook build"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@emotion/react": "^11.10.6",
|
|
@@ -57,14 +57,16 @@
|
|
|
57
57
|
"@babel/preset-typescript": "^7.21.0",
|
|
58
58
|
"@emotion/jest": "^11.10.5",
|
|
59
59
|
"@nuskin/eslint-config": "^1.0.0-setup.1",
|
|
60
|
-
"@storybook/addon-actions": "^
|
|
61
|
-
"@storybook/addon-essentials": "^
|
|
62
|
-
"@storybook/addon-interactions": "^
|
|
63
|
-
"@storybook/addon-links": "^
|
|
64
|
-
"@storybook/
|
|
65
|
-
"@storybook/
|
|
66
|
-
"@storybook/
|
|
67
|
-
"@storybook/
|
|
60
|
+
"@storybook/addon-actions": "^7.0.7",
|
|
61
|
+
"@storybook/addon-essentials": "^7.0.7",
|
|
62
|
+
"@storybook/addon-interactions": "^7.0.7",
|
|
63
|
+
"@storybook/addon-links": "^7.0.7",
|
|
64
|
+
"@storybook/addon-mdx-gfm": "^7.0.7",
|
|
65
|
+
"@storybook/jest": "^0.1.0",
|
|
66
|
+
"@storybook/nextjs": "^7.0.7",
|
|
67
|
+
"@storybook/react": "^7.0.7",
|
|
68
|
+
"@storybook/test-runner": "^0.10.0",
|
|
69
|
+
"@storybook/testing-library": "^0.1.0",
|
|
68
70
|
"@testing-library/jest-dom": "^5.16.5",
|
|
69
71
|
"@testing-library/react": "^14.0.0",
|
|
70
72
|
"@types/lodash": "^4.14.191",
|
|
@@ -77,6 +79,7 @@
|
|
|
77
79
|
"babel-jest": "^29.5.0",
|
|
78
80
|
"babel-loader": "^9.1.2",
|
|
79
81
|
"babel-plugin-emotion": "^11.0.0",
|
|
82
|
+
"chromatic": "^6.17.3",
|
|
80
83
|
"eslint": "8.36.0",
|
|
81
84
|
"eslint-config-next": "13.2.4",
|
|
82
85
|
"eslint-config-prettier": "^8.7.0",
|
|
@@ -84,9 +87,11 @@
|
|
|
84
87
|
"eslint-plugin-prettier": "^4.2.1",
|
|
85
88
|
"eslint-plugin-storybook": "^0.6.11",
|
|
86
89
|
"jest": "^29.5.0",
|
|
87
|
-
"jest-environment-jsdom": "^29.5.0"
|
|
90
|
+
"jest-environment-jsdom": "^29.5.0",
|
|
91
|
+
"storybook": "^7.0.7"
|
|
88
92
|
},
|
|
89
93
|
"files": [
|
|
90
94
|
"src/common"
|
|
91
|
-
]
|
|
95
|
+
],
|
|
96
|
+
"readme": "./README.md"
|
|
92
97
|
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, fireEvent, screen, act } from '../../../test-utils';
|
|
3
|
+
// import { act } from 'react-dom/test-utils';
|
|
4
|
+
import NsAlert from './NsAlert';
|
|
5
|
+
|
|
6
|
+
const addEventListenerMock = jest.spyOn(window, 'addEventListener');
|
|
7
|
+
const removeEventListenerMock = jest.spyOn(window, 'removeEventListener');
|
|
8
|
+
|
|
9
|
+
describe('NsAlert', () => {
|
|
10
|
+
test('renders alert with provided props', () => {
|
|
11
|
+
render(
|
|
12
|
+
<NsAlert
|
|
13
|
+
context="error"
|
|
14
|
+
title="Error Alert"
|
|
15
|
+
threshhold={800}
|
|
16
|
+
type="banner"
|
|
17
|
+
visible
|
|
18
|
+
head={<span>Alert Head</span>}
|
|
19
|
+
body={<p>Alert Body</p>}
|
|
20
|
+
foot={<span>Dismiss</span>}
|
|
21
|
+
onDismiss={() => {}}
|
|
22
|
+
/>
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
expect(screen.getByText('Error Alert')).toBeInTheDocument();
|
|
26
|
+
expect(screen.getByText('Alert Head')).toBeInTheDocument();
|
|
27
|
+
expect(screen.getByText('Alert Body')).toBeInTheDocument();
|
|
28
|
+
expect(screen.getByText('Dismiss')).toBeInTheDocument();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('calls onDismiss function when dismissed', () => {
|
|
32
|
+
const onDismissMock = jest.fn();
|
|
33
|
+
render(
|
|
34
|
+
<NsAlert
|
|
35
|
+
context="info"
|
|
36
|
+
title="Info Alert"
|
|
37
|
+
threshhold={600}
|
|
38
|
+
type="auto"
|
|
39
|
+
visible
|
|
40
|
+
head={<span>Alert Head</span>}
|
|
41
|
+
body={<p>Alert Body</p>}
|
|
42
|
+
onDismiss={onDismissMock}
|
|
43
|
+
/>
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
expect(screen.getByTestId('alert-close-button')).toBeInTheDocument();
|
|
47
|
+
const button = screen.getByTestId('alert-close-button');
|
|
48
|
+
fireEvent.click(button);
|
|
49
|
+
expect(onDismissMock).toHaveBeenCalled();
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('hides alert when not visible', () => {
|
|
53
|
+
render(
|
|
54
|
+
<NsAlert
|
|
55
|
+
context="success"
|
|
56
|
+
title="Success Alert"
|
|
57
|
+
threshhold={700}
|
|
58
|
+
type="card"
|
|
59
|
+
visible={false}
|
|
60
|
+
head={<span>Alert Head</span>}
|
|
61
|
+
body={<p>Alert Body</p>}
|
|
62
|
+
/>
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
expect(screen.getByTestId('alert-container')).toHaveStyle('display: none');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test('adds and removes event listener on mount/unmount', () => {
|
|
69
|
+
act(() => {
|
|
70
|
+
render(<NsAlert
|
|
71
|
+
context="notice"
|
|
72
|
+
title="Success Alert"
|
|
73
|
+
threshhold={700}
|
|
74
|
+
type="card"
|
|
75
|
+
visible={false}
|
|
76
|
+
head={<span>Alert Head</span>}
|
|
77
|
+
body={<p>Alert Body</p>}
|
|
78
|
+
/>);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
expect(addEventListenerMock).toHaveBeenCalledWith('resize', expect.any(Function));
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
expect(removeEventListenerMock).toHaveBeenCalledWith('resize', expect.any(Function));
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test('updates windowWidth on resize', () => {
|
|
88
|
+
const originalInnerWidth = window.innerWidth;
|
|
89
|
+
window.innerWidth = 500; // Set a desired window width for testing
|
|
90
|
+
|
|
91
|
+
act(() => {
|
|
92
|
+
render(<NsAlert />);
|
|
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
|
+
|
|
103
|
+
test('get props error in console for types', () => {
|
|
104
|
+
render(
|
|
105
|
+
<NsAlert
|
|
106
|
+
context="warning"
|
|
107
|
+
title="warning Alert"
|
|
108
|
+
threshhold={800}
|
|
109
|
+
type="noCard"
|
|
110
|
+
visible
|
|
111
|
+
head={<span>Alert Head</span>}
|
|
112
|
+
body={<p>Alert Body</p>}
|
|
113
|
+
/>
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
expect(screen.getByTestId('alert-container')).toHaveStyle('display: block');
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test('get props error in console for context', () => {
|
|
120
|
+
render(
|
|
121
|
+
<NsAlert
|
|
122
|
+
context="warningError"
|
|
123
|
+
title="warning Alert"
|
|
124
|
+
threshhold={800}
|
|
125
|
+
type="card"
|
|
126
|
+
visible
|
|
127
|
+
head={<span>Alert Head</span>}
|
|
128
|
+
body={<p>Alert Body</p>}
|
|
129
|
+
/>
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
expect(screen.getByTestId('alert-container')).toHaveStyle('display: block');
|
|
133
|
+
});
|
|
134
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
3
|
+
import NsAlert from './NsAlert';
|
|
4
|
+
import { withCenterDecorator } from '../../stories/decorators/withCenterDecorator';
|
|
5
|
+
|
|
6
|
+
type ComponentType = typeof NsAlert;
|
|
7
|
+
type Story = StoryObj<ComponentType>;
|
|
8
|
+
type MetaStory = Meta<ComponentType>;
|
|
9
|
+
|
|
10
|
+
export default {
|
|
11
|
+
title: 'Components/NsAlert',
|
|
12
|
+
component: NsAlert,
|
|
13
|
+
decorators: [withCenterDecorator],
|
|
14
|
+
argTypes: {
|
|
15
|
+
context: {
|
|
16
|
+
control: 'select',
|
|
17
|
+
options: ['error', 'info', 'notice', 'success', 'warning']
|
|
18
|
+
},
|
|
19
|
+
type: {
|
|
20
|
+
control: 'select',
|
|
21
|
+
options: ['auto', 'banner', 'card']
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
args: {
|
|
25
|
+
context: 'info',
|
|
26
|
+
title: 'Information',
|
|
27
|
+
threshhold: 600,
|
|
28
|
+
type: 'banner',
|
|
29
|
+
visible: true,
|
|
30
|
+
onDismiss: () => {},
|
|
31
|
+
body:
|
|
32
|
+
<p>{`Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
|
|
33
|
+
ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco
|
|
34
|
+
laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
|
|
35
|
+
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
|
|
36
|
+
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa
|
|
37
|
+
qui officia deserunt mollit anim id est laborum.`}</p>,
|
|
38
|
+
foot: <span>Some message goes here.</span>
|
|
39
|
+
}
|
|
40
|
+
} as MetaStory;
|
|
41
|
+
|
|
42
|
+
export const AuthDropdown: Story = {};
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/* eslint-disable max-len */
|
|
2
|
+
import { styled, css } from '@nuskin/foundation-theme';
|
|
3
|
+
import { getAppColor } from '@nuskin/foundation-theme/src/utils/color-utils';
|
|
4
|
+
import { FoundationTheme } from '@nuskin/foundation-theme/src/themes';
|
|
5
|
+
|
|
6
|
+
import type { AlertContainerParams, AlertType, AlertParams, TypeMapCSS } from './types';
|
|
7
|
+
const gutter_width= '10px';
|
|
8
|
+
const close_button_width = '12px';
|
|
9
|
+
const icon_width = '24px';
|
|
10
|
+
|
|
11
|
+
const TypeMap: TypeMapCSS = {
|
|
12
|
+
banner: {
|
|
13
|
+
'min-height' : '114px',
|
|
14
|
+
'padding' : '15px 45px',
|
|
15
|
+
'content-column-start': 'row-center',
|
|
16
|
+
'content-column-end': 'row-center',
|
|
17
|
+
'bar-position': 'top',
|
|
18
|
+
'foot-visibility': 'block'
|
|
19
|
+
},
|
|
20
|
+
card: {
|
|
21
|
+
'min-height' : '72px',
|
|
22
|
+
'padding' : '24px 24px',
|
|
23
|
+
'content-column-start': 'row-left',
|
|
24
|
+
'content-column-end': 'row-right',
|
|
25
|
+
'bar-position': 'left',
|
|
26
|
+
'foot-display': 'none',
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const contextType = {
|
|
31
|
+
error: 'red',
|
|
32
|
+
info: 'info',
|
|
33
|
+
notice: 'info',
|
|
34
|
+
success: 'success',
|
|
35
|
+
warning: 'warning'
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const AlertContainer = styled.div<AlertContainerParams>`
|
|
39
|
+
display: ${({ visible }) => visible ? 'block' : 'none'};
|
|
40
|
+
* {
|
|
41
|
+
box-sizing: border-box;
|
|
42
|
+
webkit-font-smoothing: antialiased;
|
|
43
|
+
}
|
|
44
|
+
text-align: left;
|
|
45
|
+
`;
|
|
46
|
+
|
|
47
|
+
const fontFamilyStyles = css`
|
|
48
|
+
font-family: "proxima-nova", "OpenSans", "Pridi", "NotoSans", "NotoSansSC", "NotoSansHebrew",
|
|
49
|
+
"NotoSansJapanese", "Hiragino Kaku Gothic Pro", "Yu Gothic Medium", "Yu Gothic", Meiryo, "source-han-sans-japanese",
|
|
50
|
+
"source-han-sans-korean", "source-han-sans-simplified-c", Helvetica, Arial, sans-serif;
|
|
51
|
+
`;
|
|
52
|
+
|
|
53
|
+
export const Alert = styled.div<AlertParams>`
|
|
54
|
+
position: relative;
|
|
55
|
+
display: grid;
|
|
56
|
+
grid-template-columns: [row-left] ${icon_width} [space] ${gutter_width} [row-center] auto [space] ${gutter_width} [row-right] ${close_button_width};
|
|
57
|
+
grid-template-rows: [row1] auto;
|
|
58
|
+
grid-auto-row: auto;
|
|
59
|
+
grid-auto-flow: row;
|
|
60
|
+
background-color: ${({ theme }) => getAppColor(theme, 'primary', 'white')};
|
|
61
|
+
border: 1px solid ${({ theme }) => getAppColor(theme, 'primary', 'black')};
|
|
62
|
+
min-height: ${({ type }) => TypeMap?.[type]?.['min-height']};
|
|
63
|
+
padding: ${({ type }) => TypeMap?.[type]?.['padding']};
|
|
64
|
+
color: ${({ context, theme }) => getAppColor(theme, contextType?.[context] as FoundationTheme.AppColors, '40')} };
|
|
65
|
+
`;
|
|
66
|
+
|
|
67
|
+
export const AlertCloseButton = styled.div`
|
|
68
|
+
grid-column-start: row-right;
|
|
69
|
+
grid-column-end: row-right;
|
|
70
|
+
grid-row-start: row1;
|
|
71
|
+
grid-row-end: row1;
|
|
72
|
+
align-self: center;
|
|
73
|
+
cursor: pointer;
|
|
74
|
+
`;
|
|
75
|
+
|
|
76
|
+
export const AlertIcon = styled.div`
|
|
77
|
+
grid-column-start: row-left;
|
|
78
|
+
grid-column-end: row-left;
|
|
79
|
+
grid-row-start: row1;
|
|
80
|
+
grid-row-end: row1;
|
|
81
|
+
width: ${icon_width};
|
|
82
|
+
height: ${icon_width};
|
|
83
|
+
`;
|
|
84
|
+
|
|
85
|
+
export const AlertHead = styled.div`
|
|
86
|
+
grid-column-start: row-center;
|
|
87
|
+
grid-column-end: row-center;
|
|
88
|
+
grid-row-start: row1;
|
|
89
|
+
grid-row-end: row1;
|
|
90
|
+
display: flex;
|
|
91
|
+
flex-direction: column;
|
|
92
|
+
justify-content: center;
|
|
93
|
+
span {
|
|
94
|
+
${fontFamilyStyles}
|
|
95
|
+
color: ${({ theme }) => getAppColor(theme, 'primary', 'black')};
|
|
96
|
+
font-weight: 600;
|
|
97
|
+
font-size: 18px;
|
|
98
|
+
line-height: 20px;
|
|
99
|
+
}
|
|
100
|
+
`;
|
|
101
|
+
|
|
102
|
+
export const AlertContent = styled.div<{type: AlertType}>`
|
|
103
|
+
margin-top: 8px;
|
|
104
|
+
color: ${({ theme }) => getAppColor(theme, 'primary', 'black')};
|
|
105
|
+
grid-column-start: ${({ type }) => TypeMap?.[type]?.['content-column-start']};
|
|
106
|
+
grid-column-end: ${({ type }) => TypeMap?.[type]?.['content-column-end']};
|
|
107
|
+
`;
|
|
108
|
+
|
|
109
|
+
export const AlertFoot = styled.div<{type: AlertType}>`
|
|
110
|
+
margin-top: 8px;
|
|
111
|
+
color: ${({ theme }) => getAppColor(theme, 'primary', 'black')};;
|
|
112
|
+
display: ${({ type }) => TypeMap?.[type]?.['foot-display']};
|
|
113
|
+
${fontFamilyStyles}
|
|
114
|
+
font-weight: 300;
|
|
115
|
+
font-size: 14px;
|
|
116
|
+
line-height: 18px;
|
|
117
|
+
`;
|
|
118
|
+
|
|
119
|
+
export const AlertBody = styled.div`
|
|
120
|
+
${fontFamilyStyles}
|
|
121
|
+
font-weight: 300;
|
|
122
|
+
font-size: 16px;
|
|
123
|
+
line-height: 21px;
|
|
124
|
+
`;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/* eslint-disable react/require-default-props */
|
|
2
|
+
import React, { useEffect, useState } from 'react';
|
|
3
|
+
import {
|
|
4
|
+
AlertContainer,
|
|
5
|
+
Alert,
|
|
6
|
+
AlertIcon,
|
|
7
|
+
AlertHead,
|
|
8
|
+
AlertCloseButton,
|
|
9
|
+
AlertContent,
|
|
10
|
+
AlertFoot,
|
|
11
|
+
AlertBody
|
|
12
|
+
} from './NsAlert.styled'
|
|
13
|
+
import type { NsAlertProps, HeaderProps, ContextType, AlertType } from './types';
|
|
14
|
+
import { NsIcon } from '../icon';
|
|
15
|
+
import { NsIconNameType } from '../icon/types'
|
|
16
|
+
import { FoundationTheme } from '@nuskin/foundation-theme/src/themes';
|
|
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
|
+
};
|
|
47
|
+
|
|
48
|
+
export default function NsAlert({
|
|
49
|
+
context = 'info',
|
|
50
|
+
title = 'Information',
|
|
51
|
+
threshhold = 600,
|
|
52
|
+
type = 'banner',
|
|
53
|
+
visible = false,
|
|
54
|
+
head,
|
|
55
|
+
body,
|
|
56
|
+
foot,
|
|
57
|
+
onDismiss = undefined
|
|
58
|
+
}: NsAlertProps) {
|
|
59
|
+
const [alertType, setAlertType] = useState('banner');
|
|
60
|
+
const [windowWidth, setWindowWidth] = useState(window.innerWidth);
|
|
61
|
+
|
|
62
|
+
const hasDismissListener = typeof onDismiss === 'function';
|
|
63
|
+
|
|
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
|
+
useEffect(() => {
|
|
99
|
+
if (!isValidContextValue(context)) {
|
|
100
|
+
console.error(`Invalid prop value for 'context': ${context}`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (!isValidTypeValue(type)) {
|
|
104
|
+
console.error(`Invalid prop value for 'type': ${type}`);
|
|
105
|
+
}
|
|
106
|
+
}, [context, type]);
|
|
107
|
+
|
|
108
|
+
useEffect(() => {
|
|
109
|
+
window.addEventListener('resize', handleResize);
|
|
110
|
+
return () => {
|
|
111
|
+
window.removeEventListener('resize', handleResize);
|
|
112
|
+
};
|
|
113
|
+
}, []);
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<AlertContainer visible={visible} data-testid="alert-container">
|
|
117
|
+
<Alert type={alertType as AlertType} context={context as ContextType}>
|
|
118
|
+
<AlertIcon>
|
|
119
|
+
<NsIcon
|
|
120
|
+
name={getIcon()}
|
|
121
|
+
color={getColor()}
|
|
122
|
+
size="large"
|
|
123
|
+
/>
|
|
124
|
+
</AlertIcon>
|
|
125
|
+
<AlertHead>
|
|
126
|
+
<Header head={<span>{getTitleText()}</span>}>
|
|
127
|
+
{head}
|
|
128
|
+
</Header>
|
|
129
|
+
</AlertHead>
|
|
130
|
+
{
|
|
131
|
+
hasDismissListener ? (
|
|
132
|
+
<AlertCloseButton data-testid="alert-close-button" onClick={handleDismiss}>
|
|
133
|
+
<NsIcon
|
|
134
|
+
name='close'
|
|
135
|
+
size='small'
|
|
136
|
+
/>
|
|
137
|
+
</AlertCloseButton>
|
|
138
|
+
) : null
|
|
139
|
+
}
|
|
140
|
+
<AlertContent type={alertType as AlertType}>
|
|
141
|
+
<AlertBody>
|
|
142
|
+
{body}
|
|
143
|
+
</AlertBody>
|
|
144
|
+
{
|
|
145
|
+
foot ? (
|
|
146
|
+
<AlertFoot type={alertType as AlertType}>
|
|
147
|
+
{foot}
|
|
148
|
+
</AlertFoot>
|
|
149
|
+
) : null
|
|
150
|
+
}
|
|
151
|
+
</AlertContent>
|
|
152
|
+
</Alert>
|
|
153
|
+
</AlertContainer>
|
|
154
|
+
);
|
|
155
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { ReactNode, CSSProperties } from 'react';
|
|
2
|
+
|
|
3
|
+
export interface NsAlertProps {
|
|
4
|
+
context?: string,
|
|
5
|
+
title?: string,
|
|
6
|
+
threshhold?: number,
|
|
7
|
+
type?: string,
|
|
8
|
+
visible?: boolean,
|
|
9
|
+
head?: ReactNode,
|
|
10
|
+
body?: ReactNode,
|
|
11
|
+
onDismiss?: () => void,
|
|
12
|
+
foot?: ReactNode
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface HeaderProps {
|
|
16
|
+
children?: ReactNode; // Default slot content
|
|
17
|
+
head?: ReactNode; // Content for the "head" named slot (optional)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type AlertContainerParams = {
|
|
21
|
+
visible: boolean
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export type ContextType = 'error' | 'info' | 'notice'| 'success' | 'warning';
|
|
25
|
+
export type AlertType = 'auto' | 'banner' | 'card';
|
|
26
|
+
|
|
27
|
+
export type AlertParams = {
|
|
28
|
+
context: ContextType,
|
|
29
|
+
type: AlertType
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type CustomCSSProperties = CSSProperties & {
|
|
33
|
+
'min-height'?: string;
|
|
34
|
+
'content-column-start'? : string;
|
|
35
|
+
'content-column-end'? : string;
|
|
36
|
+
'bar-position'? : string;
|
|
37
|
+
'foot-visibility'? : string;
|
|
38
|
+
'foot-display'? : string;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export interface TypeMapCSS {
|
|
42
|
+
banner: CustomCSSProperties,
|
|
43
|
+
card: CustomCSSProperties,
|
|
44
|
+
auto?: CustomCSSProperties
|
|
45
|
+
}
|
|
@@ -1,11 +1,15 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
3
2
|
import { NsAuthDropdown } from './';
|
|
3
|
+
import { withCenterDecorator } from '../../stories/decorators/withCenterDecorator';
|
|
4
|
+
|
|
5
|
+
type ComponentType = typeof NsAuthDropdown;
|
|
6
|
+
type Story = StoryObj<ComponentType>;
|
|
7
|
+
type MetaStory = Meta<ComponentType>;
|
|
4
8
|
|
|
5
9
|
export default {
|
|
6
10
|
title: 'Components/NsAuthDropdown',
|
|
7
11
|
component: NsAuthDropdown,
|
|
8
|
-
decorators: [
|
|
12
|
+
decorators: [withCenterDecorator],
|
|
9
13
|
argTypes: {},
|
|
10
14
|
args: {
|
|
11
15
|
signInRedirectLink: '/signIn',
|
|
@@ -16,6 +20,6 @@ export default {
|
|
|
16
20
|
titleText: 'SIGN UP / SIGN IN',
|
|
17
21
|
closeOnMouseLeave: true,
|
|
18
22
|
},
|
|
19
|
-
} as
|
|
23
|
+
} as MetaStory;
|
|
20
24
|
|
|
21
|
-
export const AuthDropdown:
|
|
25
|
+
export const AuthDropdown: Story = {};
|
|
@@ -37,7 +37,7 @@ const NsAuthDropdown = ({
|
|
|
37
37
|
<AuthDropdownContainer>
|
|
38
38
|
<AuthHead data-testid="auth-head" onClick={handleToggle}>
|
|
39
39
|
<Button>{titleText}</Button>
|
|
40
|
-
<NsIcon testId="chevron-icon" name={isOpen ? '
|
|
40
|
+
<NsIcon testId="chevron-icon" name={isOpen ? 'arrowUp' : 'arrowDown'} color="primary" />
|
|
41
41
|
{isOpen ? (
|
|
42
42
|
<div onMouseLeave={closeDropdown}>
|
|
43
43
|
<Triangle />
|