@nuskin/foundation-ui-components 1.0.0-develop.8 → 1.0.0-develop.9

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nuskin/foundation-ui-components",
3
- "version": "1.0.0-develop.8",
3
+ "version": "1.0.0-develop.9",
4
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",
@@ -44,6 +44,8 @@ const NsAccordion: FC<NsAccordionProps> = ({
44
44
  const isOpen = itemStateMap[key] ?? false;
45
45
  return (
46
46
  <NsAccordionItem
47
+ allowMultipleExpanded={item.allowMultipleExpanded}
48
+ type={item.type}
47
49
  closedIconName={closedIconName}
48
50
  content={item.content}
49
51
  headerTypographyVariant={headerTypographyVariant}
@@ -43,6 +43,7 @@ const NsAccordionHeader = ({
43
43
  headerTypographyVariant,
44
44
  startIcon,
45
45
  invert,
46
+ cslp,
46
47
  }: NsAccordionHeaderProps) => {
47
48
  return (
48
49
  <StyledHeader
@@ -55,7 +56,7 @@ const NsAccordionHeader = ({
55
56
  >
56
57
  <HeaderIcon {...startIcon} />
57
58
  {typeof title === 'string' ? (
58
- <NsTypography variant={headerTypographyVariant ?? 'h4'} weight="bold">
59
+ <NsTypography variant={headerTypographyVariant ?? 'h4'} weight="bold" {...(cslp?.header ?? {})}>
59
60
  {title}
60
61
  </NsTypography>
61
62
  ) : (
@@ -6,6 +6,7 @@ import { StyledToggleSection } from './ToggleSection.styled';
6
6
  /**
7
7
  * this component is used by accordion to pass title
8
8
  * and content
9
+ * cslp:{header:CslpTag, content:CslpTag}
9
10
  */
10
11
  const NsAccordionItem = ({
11
12
  isOpen,
@@ -21,6 +22,9 @@ const NsAccordionItem = ({
21
22
  level,
22
23
  additionalProps,
23
24
  invert,
25
+ type,
26
+ cslp,
27
+ allowMultipleExpanded,
24
28
  }: NsAccordionItemProps) => {
25
29
  return (
26
30
  <StyledToggleSection variant={variant}>
@@ -35,14 +39,18 @@ const NsAccordionItem = ({
35
39
  title={title}
36
40
  variant={variant}
37
41
  startIcon={startIcon}
42
+ cslp={cslp}
38
43
  />
39
44
  <NsCollapse
45
+ type={type}
40
46
  isChild={isChild}
41
47
  isOpen={isOpen}
42
48
  variant={variant}
43
49
  level={level}
44
50
  invert={invert}
45
51
  content={content}
52
+ cslp={cslp}
53
+ allowMultipleExpanded={allowMultipleExpanded}
46
54
  />
47
55
  </StyledToggleSection>
48
56
  );
@@ -1,5 +1,5 @@
1
1
  import { styled } from '@nuskin/foundation-theme';
2
- import { AccordionVariant, AccordionVariantType, NsCollapseProps, ItemContent, AccordionItem, NsAccordion } from '../';
2
+ import { AccordionVariant, AccordionVariantType, NsCollapseProps, AccordionItem, NsAccordion } from '../';
3
3
  import { getAccordionColor } from './colorMap';
4
4
  import { ReactElement, isValidElement, useMemo } from 'react';
5
5
 
@@ -52,31 +52,49 @@ export const StyledCollapse = styled(
52
52
  * NsCollapse component adds css animation while showing children items if prop `isOpen` is true,
53
53
  * and while removing children if prop `isOpen` is false
54
54
  */
55
- const NsCollapse = ({ isOpen, content, additionalProps, variant, level, invert, isChild }: NsCollapseProps) => {
55
+ const NsCollapse = ({
56
+ isOpen,
57
+ content,
58
+ additionalProps,
59
+ variant,
60
+ level,
61
+ invert,
62
+ isChild,
63
+ type,
64
+ cslp,
65
+ allowMultipleExpanded,
66
+ }: NsCollapseProps) => {
56
67
  const body = useMemo(() => {
57
68
  if (isValidElement(content)) {
58
69
  return content as ReactElement;
59
70
  }
60
- const contentVar = content as unknown as ItemContent;
61
- if (contentVar?.type === 'html') {
71
+ if (type === 'html') {
62
72
  return (
63
- <div className="nsa-content-padded" dangerouslySetInnerHTML={{ __html: contentVar?.data as string }} />
73
+ <div
74
+ className="nsa-content-padded"
75
+ dangerouslySetInnerHTML={{ __html: content as string }}
76
+ {...(cslp?.content ?? {})}
77
+ />
78
+ );
79
+ } else if (type === 'text') {
80
+ return (
81
+ <div className="p-4" {...(cslp?.content ?? {})}>
82
+ {content as string}
83
+ </div>
64
84
  );
65
- } else if (contentVar?.type === 'text') {
66
- return <div className="p-4">{contentVar?.data as string}</div>;
67
- } else if (contentVar?.type === 'accordion') {
85
+ } else if (type === 'accordion') {
68
86
  return (
69
87
  <NsAccordion
70
88
  isChild={true}
71
89
  invert={invert}
72
90
  level={level}
73
91
  variant={AccordionVariant.Gray}
74
- items={contentVar?.data as AccordionItem[]}
75
- allowMultipleExpanded={contentVar?.allowMultipleExpanded ?? false}
92
+ items={content as AccordionItem[]}
93
+ allowMultipleExpanded={allowMultipleExpanded ?? false}
76
94
  />
77
95
  );
78
96
  }
79
- }, [content, invert, level]);
97
+ }, [allowMultipleExpanded, content, cslp?.content, invert, level, type]);
80
98
  return (
81
99
  <StyledCollapse
82
100
  isChild={isChild}
@@ -17,6 +17,7 @@ const NsToggleSection = ({
17
17
  variant,
18
18
  invert,
19
19
  isChild,
20
+ type,
20
21
  }: NsToggleSectionProps) => {
21
22
  const [isExpanded, setIsExpanded] = useState(defaultOpen ?? false);
22
23
 
@@ -25,6 +26,7 @@ const NsToggleSection = ({
25
26
  };
26
27
  return (
27
28
  <NsAccordionItem
29
+ type={type}
28
30
  level={2}
29
31
  variant={variant}
30
32
  additionalProps={additionalProps}
@@ -9,6 +9,7 @@ const generateContentArray = (count: number, isDefaultOpen = true) => {
9
9
  title: `Title ${i}`,
10
10
  content: <div className="p-2">Content {i}</div>,
11
11
  isDefaultOpen,
12
+ type:'jsx',
12
13
  id: `Test ${i}`,
13
14
  });
14
15
  }
@@ -9,6 +9,7 @@ describe('NsAccordionItem', () => {
9
9
  it('1. renders title correctly', () => {
10
10
  const { getByText } = render(
11
11
  <NsAccordionItem
12
+ type="jsx"
12
13
  variant={AccordionVariant.Basic}
13
14
  isOpen={false}
14
15
  toggleOpen={() => {}}
@@ -26,6 +27,7 @@ describe('NsAccordionItem', () => {
26
27
  const toggleOpen = jest.fn();
27
28
  const { getByText } = render(
28
29
  <NsAccordionItem
30
+ type="jsx"
29
31
  variant={AccordionVariant.Basic}
30
32
  isOpen={false}
31
33
  toggleOpen={toggleOpen}
@@ -47,6 +49,7 @@ describe('NsAccordionItem', () => {
47
49
  isOpen={true}
48
50
  toggleOpen={() => {}}
49
51
  title={title}
52
+ type="jsx"
50
53
  content={contentElement}
51
54
  isChild={false}
52
55
  invert={false}
@@ -59,6 +62,7 @@ describe('NsAccordionItem', () => {
59
62
  it('4. when isOpen is false, renders jsx dom content within container with 0 height', () => {
60
63
  const { queryByText } = render(
61
64
  <NsAccordionItem
65
+ type="jsx"
62
66
  variant={AccordionVariant.Basic}
63
67
  isOpen={false}
64
68
  toggleOpen={() => {}}
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import { render, screen } from '@/test-utils/index';
3
- import { NsCollapse, AccordionVariant, ItemContent } from '@/common/index';
4
- import { createAccordionItem3_1, getTextContent } from '../stories/data';
3
+ import { NsCollapse, AccordionVariant, AccordionItem } from '@/common/index';
4
+ import { createAccordionItem3_1 } from '../stories/data';
5
5
 
6
6
  describe('NsCollapse', () => {
7
7
  it('1. renders children when isOpen is true', () => {
@@ -15,6 +15,7 @@ describe('NsCollapse', () => {
15
15
  <div>Child 2</div>
16
16
  </>
17
17
  }
18
+ type="jsx"
18
19
  isChild={false}
19
20
  invert={false}
20
21
  level={0}
@@ -24,11 +25,11 @@ describe('NsCollapse', () => {
24
25
  expect(getByText('Child 2')).toBeInTheDocument();
25
26
  });
26
27
  it('2. renders children when isOpen is true and isChild is true', () => {
27
- const text = '1. Theme can be set';
28
- const content = { ...getTextContent(text) };
28
+ const content = 'renders children when isOpen is true and isChild is true';
29
29
  const { getByText } = render(
30
30
  <NsCollapse
31
31
  isOpen
32
+ type="text"
32
33
  variant={AccordionVariant.Basic}
33
34
  content={content}
34
35
  isChild={true}
@@ -36,60 +37,62 @@ describe('NsCollapse', () => {
36
37
  level={0}
37
38
  />
38
39
  );
39
- expect(getByText(text)).toBeInTheDocument();
40
+ expect(getByText(content)).toBeInTheDocument();
40
41
  });
41
42
  it('3. renders children when isOpen is true and variant is compact and content is of type accordion', () => {
43
+ const accItem = createAccordionItem3_1();
44
+ const content = accItem.content as AccordionItem[];
42
45
  const { getByText } = render(
43
46
  <NsCollapse
44
47
  isOpen
45
48
  variant={AccordionVariant.Compact}
46
- content={createAccordionItem3_1().content}
49
+ content={content}
50
+ type={accItem.type}
47
51
  isChild={true}
48
52
  invert={false}
49
53
  level={0}
50
54
  />
51
55
  );
52
- expect(getByText('Example - 3.1.1')).toBeInTheDocument();
53
- expect(getByText('Example - 3.1.2')).toBeInTheDocument();
56
+ expect(getByText(content[0].title)).toBeInTheDocument();
57
+ expect(getByText(content[1].title)).toBeInTheDocument();
54
58
  });
55
59
 
56
60
  it('4. does not render children when isOpen is false', () => {
61
+ const content = '<h2>Lorem ipsum dolor sit amet.</h2>';
57
62
  render(
58
63
  <NsCollapse
59
64
  isOpen={false}
60
65
  variant={AccordionVariant.Basic}
61
- content={{
62
- type: 'html',
63
- data: '<h2>Lorem ipsum dolor sit amet.</h2>',
64
- }}
66
+ type="html"
67
+ content={content}
65
68
  isChild={false}
66
69
  invert={true}
67
70
  level={0}
68
71
  />
69
72
  );
70
- expect(screen.queryByText('Lorenm')).toBeNull();
73
+ expect(screen.queryByText(content)).toBeNull();
71
74
  });
72
75
  it('5. renders html children when isOpen is true', () => {
76
+ const content = '<h2>Lorem ipsum dolor sit amet.</h2>';
73
77
  render(
74
78
  <NsCollapse
75
79
  isOpen={true}
76
80
  variant={AccordionVariant.Compact}
77
- content={{
78
- type: 'html',
79
- data: '<h2>Lorem ipsum dolor sit amet.</h2>',
80
- }}
81
+ type="html"
82
+ content={content}
81
83
  isChild={false}
82
84
  invert={true}
83
85
  level={0}
84
86
  />
85
87
  );
86
- expect(screen.queryByText('Lorem')).toBeDefined();
88
+ expect(screen.queryByText(content)).toBeDefined();
87
89
  });
88
90
 
89
91
  it('6. renders with additional class name', () => {
90
92
  const additionalClassName = 'custom-class';
91
93
  const { container } = render(
92
94
  <NsCollapse
95
+ type="jsx"
93
96
  isOpen
94
97
  variant={AccordionVariant.Gray}
95
98
  content={
@@ -107,11 +110,11 @@ describe('NsCollapse', () => {
107
110
  expect(container.querySelector('section')).toHaveClass(`ns-collapse ${additionalClassName}`);
108
111
  });
109
112
  it('7. skipping allowMultipleExpanded in child data', () => {
110
- const { content } = createAccordionItem3_1();
111
- delete (content as ItemContent).allowMultipleExpanded;
113
+ const { content, type } = createAccordionItem3_1();
112
114
  const additionalClassName = 'custom-class';
113
115
  const { container } = render(
114
116
  <NsCollapse
117
+ type={type}
115
118
  isOpen
116
119
  variant={AccordionVariant.Gray}
117
120
  content={content}
@@ -16,6 +16,7 @@ describe('NsToggleSection', () => {
16
16
  content={content}
17
17
  invert={false}
18
18
  isChild={false}
19
+ type="jsx"
19
20
  />
20
21
  );
21
22
  expect(getByText(title)).toBeInTheDocument();
@@ -28,6 +29,7 @@ describe('NsToggleSection', () => {
28
29
  content={content}
29
30
  invert={false}
30
31
  isChild={false}
32
+ type="jsx"
31
33
  />
32
34
  );
33
35
  expect(getByText(title)).toBeInTheDocument();
@@ -41,6 +43,7 @@ describe('NsToggleSection', () => {
41
43
  content={content}
42
44
  invert={false}
43
45
  isChild={false}
46
+ type="jsx"
44
47
  />
45
48
  );
46
49
  const el = queryByText(contentText);
@@ -62,6 +65,7 @@ describe('NsToggleSection', () => {
62
65
  invert={false}
63
66
  isChild={false}
64
67
  defaultOpen
68
+ type="jsx"
65
69
  />
66
70
  );
67
71
 
@@ -1,6 +1,6 @@
1
1
  import type { StoryObj, Meta } from '@storybook/react';
2
2
  import { withCenterDecorator } from '@/stories/decorators/withCenterDecorator';
3
- import { NsAccordionHeader, NsIconVariants } from '../..';
3
+ import { AccordionVariant, NsAccordionHeader, NsIconVariants } from '../..';
4
4
 
5
5
  type ComponentType = typeof NsAccordionHeader;
6
6
  type Story = StoryObj<ComponentType>;
@@ -23,6 +23,7 @@ export default {
23
23
  },
24
24
  args: {
25
25
  title: 'Nuskin',
26
+ variant: AccordionVariant.Compact,
26
27
  onClick: () => {
27
28
  console.log('accordion header clicked');
28
29
  },
@@ -1,6 +1,7 @@
1
1
  import type { StoryObj, Meta } from '@storybook/react';
2
2
  import { withCenterDecorator } from '@/stories/decorators/withCenterDecorator';
3
- import { NsAccordionItem } from '@/common/index';
3
+ import { AccordionVariant, NsAccordionItem } from '../../';
4
+ import NsAccordionStory from './NsAccordion.stories';
4
5
 
5
6
  type ComponentType = typeof NsAccordionItem;
6
7
  type Story = StoryObj<ComponentType>;
@@ -20,11 +21,14 @@ export default {
20
21
  control: 'string',
21
22
  description: 'text used for header of section',
22
23
  },
24
+ level: NsAccordionStory.argTypes.level,
25
+ variant: NsAccordionStory.argTypes.variant,
23
26
  },
24
27
  } as MetaStory;
25
28
 
26
29
  export const OpenSection: Story = {
27
30
  args: {
31
+ variant: AccordionVariant.Compact,
28
32
  isOpen: true,
29
33
  content: <div className="p-2">Nuskin Description</div>,
30
34
  title: 'Nuskin',
@@ -33,6 +37,7 @@ export const OpenSection: Story = {
33
37
 
34
38
  export const ClosedSection: Story = {
35
39
  args: {
40
+ variant: AccordionVariant.Compact,
36
41
  isOpen: false,
37
42
  content: <div className="p-2">Nuskin Description</div>,
38
43
  title: 'Nuskin',
@@ -1,6 +1,7 @@
1
1
  import type { StoryObj, Meta } from '@storybook/react';
2
2
  import { withCenterDecorator } from '@/stories/decorators/withCenterDecorator';
3
- import { NsCollapse } from '../';
3
+ import { AccordionVariant, NsCollapse } from '../';
4
+ import NsAccordionStory from './NsAccordion.stories';
4
5
 
5
6
  type ComponentType = typeof NsCollapse;
6
7
  type Story = StoryObj<ComponentType>;
@@ -15,10 +16,24 @@ export default {
15
16
  control: 'boolean',
16
17
  description: 'if true renders NsCollapse in open state, and close state otherwise',
17
18
  },
18
- children: {
19
+ content: {
19
20
  control: 'React Node',
20
21
  description: 'Content for NsCollapse',
21
22
  },
23
+ allowMultipleExpanded: {
24
+ control: 'boolean',
25
+ description: 'This prop is useful when content is an array of accordion items',
26
+ },
27
+ type: {
28
+ control: 'select',
29
+ values: ['jsx', 'html', 'text', 'accordion'],
30
+ description: 'This type should exactly match the content type to have accordion work correctly',
31
+ },
32
+ level: NsAccordionStory.argTypes.level,
33
+ variant: NsAccordionStory.argTypes.variant,
34
+ },
35
+ args: {
36
+ variant: AccordionVariant.Compact,
22
37
  },
23
38
  } as MetaStory;
24
39
  export const Closed: Story = {
@@ -1,6 +1,7 @@
1
1
  import type { StoryObj, Meta } from '@storybook/react';
2
2
  import { withCenterDecorator } from '@/stories/decorators/withCenterDecorator';
3
- import { NsToggleSection } from '../..';
3
+ import { AccordionVariant, NsToggleSection } from '../..';
4
+ import NsAccordionStory from './NsAccordion.stories';
4
5
 
5
6
  type ComponentType = typeof NsToggleSection;
6
7
  type Story = StoryObj<ComponentType>;
@@ -11,9 +12,12 @@ export default {
11
12
  component: NsToggleSection,
12
13
  decorators: [withCenterDecorator],
13
14
  argTypes: {
15
+ level: NsAccordionStory.argTypes.level,
16
+ variant: NsAccordionStory.argTypes.variant,
14
17
  title: { control: 'string' },
15
18
  },
16
19
  args: {
20
+ variant: AccordionVariant.Compact,
17
21
  title: 'Nuskin',
18
22
  content: <div className="p-2">Nuskin Details</div>,
19
23
  },
@@ -1,5 +1,5 @@
1
1
  import { NsIconProps } from '../..';
2
- import { AccordionItem, ItemContent } from '../types';
2
+ import { AccordionItem } from '../types';
3
3
 
4
4
  const getItem = () => {
5
5
  return {
@@ -14,8 +14,8 @@ const getIcon = (name = '', color = '') => {
14
14
  };
15
15
  export const getTextContent = (text = '') => {
16
16
  return {
17
- type: 'text' as ItemContent['type'],
18
- data: text || dummyText,
17
+ type: 'text' as AccordionItem['type'],
18
+ content: text || dummyText,
19
19
  };
20
20
  };
21
21
  const getJsxContent = (text = '') => {
@@ -33,6 +33,7 @@ function createAccordionItem1(): AccordionItem {
33
33
  icon: getIcon(),
34
34
  title: 'Example - 1 JSX',
35
35
  content: getJsxContent(),
36
+ type: 'jsx',
36
37
  isDefaultOpen: true,
37
38
  };
38
39
  }
@@ -42,18 +43,17 @@ function createAccordionItem2(): AccordionItem {
42
43
  isDefaultOpen: true,
43
44
  icon: getIcon(),
44
45
  title: 'Example - 2',
45
- content: {
46
- type: 'accordion',
47
- data: [
48
- { ...getItem(), title: 'Example - 2.1', content: getTextContent() },
49
- {
50
- ...getItem(),
51
- icon: getIcon('hamburgerIcon', 'success'),
52
- title: 'Example - 2.2',
53
- content: <div className="p-2">Example 2.2 Content</div>,
54
- },
55
- ],
56
- },
46
+ type: 'accordion',
47
+ content: [
48
+ { ...getItem(), title: 'Example - 2.1', ...getTextContent() },
49
+ {
50
+ ...getItem(),
51
+ icon: getIcon('hamburgerIcon', 'success'),
52
+ title: 'Example - 2.2',
53
+ type: 'jsx',
54
+ content: <div className="p-2">Example 2.2 Content</div>,
55
+ },
56
+ ],
57
57
  };
58
58
  }
59
59
  function createAccordionItem3(): AccordionItem {
@@ -61,21 +61,17 @@ function createAccordionItem3(): AccordionItem {
61
61
  ...getItem(),
62
62
  icon: getIcon('hamburgerIcon', 'success'),
63
63
  title: 'Example - 3',
64
- content: {
65
- type: 'accordion',
66
- data: [
67
- createAccordionItem3_1(),
68
- {
69
- ...getItem(),
64
+ type: 'accordion',
65
+ content: [
66
+ createAccordionItem3_1(),
67
+ {
68
+ ...getItem(),
70
69
 
71
- title: 'Example - 3.2',
72
- content: {
73
- type: 'text',
74
- data: dummyText,
75
- },
76
- },
77
- ],
78
- },
70
+ title: 'Example - 3.2',
71
+ type: 'text',
72
+ content: dummyText,
73
+ },
74
+ ],
79
75
  };
80
76
  }
81
77
 
@@ -83,28 +79,22 @@ export function createAccordionItem3_1(): AccordionItem {
83
79
  return {
84
80
  ...getItem(),
85
81
  title: 'Example - 3.1',
86
- content: {
87
- type: 'accordion',
88
- allowMultipleExpanded: true,
89
- data: [
90
- {
91
- ...getItem(),
92
- title: 'Example - 3.1.1',
93
- content: {
94
- type: 'text',
95
- data: dummyText,
96
- },
97
- },
98
- {
99
- ...getItem(),
100
- title: 'Example - 3.1.2',
101
- content: {
102
- type: 'text',
103
- data: dummyText,
104
- },
105
- },
106
- ],
107
- },
82
+ allowMultipleExpanded: true,
83
+ type: 'accordion',
84
+ content: [
85
+ {
86
+ ...getItem(),
87
+ title: 'Example - 3.1.1',
88
+ type: 'text',
89
+ content: dummyText,
90
+ },
91
+ {
92
+ ...getItem(),
93
+ title: 'Example - 3.1.2',
94
+ type: 'text',
95
+ content: dummyText,
96
+ },
97
+ ],
108
98
  };
109
99
  }
110
100
 
@@ -113,20 +103,16 @@ function createAccordionItem4(): AccordionItem {
113
103
  ...getItem(),
114
104
  icon: getIcon(),
115
105
  title: 'Example - 4',
116
- content: {
117
- type: 'accordion',
118
- data: [
119
- createAccordionItem4_1(),
120
- {
121
- ...getItem(),
122
- title: 'Example - 4.2',
123
- content: {
124
- type: 'html',
125
- data: '<h2>Lorem ipsum dolor sit amet.</h2>',
126
- },
127
- },
128
- ],
129
- },
106
+ type: 'accordion',
107
+ content: [
108
+ createAccordionItem4_1(),
109
+ {
110
+ ...getItem(),
111
+ title: 'Example - 4.2',
112
+ type: 'html',
113
+ content: '<h2>Lorem ipsum dolor sit amet.</h2>',
114
+ },
115
+ ],
130
116
  };
131
117
  }
132
118
 
@@ -134,25 +120,22 @@ function createAccordionItem4_1(): AccordionItem {
134
120
  return {
135
121
  ...getItem(),
136
122
  title: 'Example - 4.1',
137
- content: {
138
- type: 'accordion',
139
- data: [
140
- {
141
- ...getItem(),
123
+ type: 'accordion',
124
+ content: [
125
+ {
126
+ ...getItem(),
142
127
 
143
- title: 'Example - 4.1.1',
144
- content: {
145
- type: 'text',
146
- data: dummyText,
147
- },
148
- },
149
- {
150
- ...getItem(),
151
- content: <div className="p-3">Example 4.1.2. content</div>,
152
- title: 'Example - 4.1.2',
153
- },
154
- ],
155
- },
128
+ title: 'Example - 4.1.1',
129
+ type: 'text',
130
+ content: dummyText,
131
+ },
132
+ {
133
+ ...getItem(),
134
+ type: 'jsx',
135
+ content: <div className="p-3">Example 4.1.2. content</div>,
136
+ title: 'Example - 4.1.2',
137
+ },
138
+ ],
156
139
  };
157
140
  }
158
141
  export function getAccordionData(): AccordionItem[] {
@@ -1,18 +1,14 @@
1
- import {ReactElement, ReactNode} from 'react';
2
- import {AdditionalProps} from '../common.types';
3
- import {NsIconNameType, NsIconProps} from '../icon';
4
- import {Variants} from '../typography';
5
- import {AccordionVariantType} from './enums';
6
-
7
- export interface ItemContent {
8
- type: 'text' | 'html' | 'accordion' | 'jsx';
9
- allowMultipleExpanded?: boolean;
10
- data: string | AccordionItem[];
11
- }
1
+ import { ReactElement, ReactNode } from 'react';
2
+ import { AdditionalProps } from '../common.types';
3
+ import { NsIconNameType, NsIconProps } from '../icon';
4
+ import { Variants } from '../typography';
5
+ import { AccordionVariantType } from './enums';
12
6
 
13
7
  export type AccordionItem = {
8
+ allowMultipleExpanded?: boolean;
9
+ type: 'text' | 'html' | 'accordion' | 'jsx';
14
10
  id: string;
15
- content: ItemContent | ReactElement;
11
+ content: string | AccordionItem[] | ReactElement;
16
12
  icon?: NsIconProps;
17
13
  title: string;
18
14
  isDefaultOpen?: boolean;
@@ -56,6 +52,8 @@ export type NsAccordionHeaderProps = AdditionalProps & {
56
52
  export type NsCollapseProps = AdditionalProps & {
57
53
  isOpen: boolean;
58
54
  isChild: boolean;
55
+ allowMultipleExpanded?: boolean;
56
+ type: 'text' | 'html' | 'accordion' | 'jsx';
59
57
  } & CommonAccordionType;
60
58
 
61
59
  export type NsToggleSectionProps = AdditionalProps & {
@@ -63,6 +61,7 @@ export type NsToggleSectionProps = AdditionalProps & {
63
61
  /** if true, NsToggle is rendered in open state, that collapsible section is visible */
64
62
  defaultOpen?: boolean;
65
63
  isChild: boolean;
64
+ type: 'text' | 'html' | 'accordion' | 'jsx';
66
65
  } & Pick<CommonAccordionType, 'invert' | 'variant' | 'content'>;
67
66
 
68
67
  export type NsAccordionItemProps = AdditionalProps & {
@@ -77,4 +76,6 @@ export type NsAccordionItemProps = AdditionalProps & {
77
76
  closedIconName?: NsIconNameType;
78
77
  startIcon?: NsIconProps;
79
78
  isChild: boolean;
79
+ type: 'text' | 'html' | 'accordion' | 'jsx';
80
+ allowMultipleExpanded?: boolean;
80
81
  } & CommonAccordionType;
@@ -2,15 +2,21 @@ import { NsNextImage, NsLink } from '../';
2
2
  import { NsFfgStyled } from './styled/NsFfgStyled';
3
3
  import { NsFfgCardProps } from './types.d';
4
4
 
5
- const NsFfgCard = ({ actionText, actionLink, text, topImageUrl, topImageAlt, additionalProps }: NsFfgCardProps) => {
5
+ const NsFfgCard = ({ cardLink, text, topImageUrl, topImageAlt, additionalProps, cslp, onClick }: NsFfgCardProps) => {
6
6
  return (
7
- <NsFfgStyled data-testid={additionalProps?.testid}>
8
- <div className="top-image">
7
+ <NsFfgStyled
8
+ data-testid={additionalProps?.testid}
9
+ onClick={onClick}
10
+ className={additionalProps?.className ?? ''}
11
+ >
12
+ <header className="top-image">
9
13
  <NsNextImage height={192} width={288} alt={topImageAlt} src={topImageUrl} />
10
- </div>
11
- <div className="body-text">{text}</div>
12
- <NsLink className="action-link" href={actionLink}>
13
- {actionText || 'LEARN MORE'}
14
+ </header>
15
+ <article className="body-text" {...cslp?.card_text}>
16
+ {text}
17
+ </article>
18
+ <NsLink className="action-link" href={cardLink.href} additionalProps={cslp?.action_text}>
19
+ {cardLink.title || 'LEARN MORE'}
14
20
  </NsLink>
15
21
  </NsFfgStyled>
16
22
  );
@@ -1,4 +1,4 @@
1
- import { render, screen } from '@testing-library/react';
1
+ import { render, screen, fireEvent } from '@testing-library/react';
2
2
  import NsFfgCard from '../NsFfgCard';
3
3
 
4
4
  describe('NsFfgCard component', () => {
@@ -6,18 +6,28 @@ describe('NsFfgCard component', () => {
6
6
  topImageUrl: 'https://media.nuskin.com/m/6245528b8f76ad32/webimage-brand-ageLOC.png',
7
7
  topImageAlt: 'alt-text',
8
8
  text: 'card text',
9
- actionLink: 'https://www.google.com',
9
+ cardLink: { href: 'https://www.google.com', title: 'Test' },
10
+ onClick: jest.fn(),
10
11
  };
11
- it('renders with background image', () => {
12
+ it('1. renders with background image', () => {
12
13
  render(<NsFfgCard {...props} additionalProps={{ testid: 'ffg-card' }} />);
13
14
  const card = screen.getByTestId('ffg-card');
14
15
  expect(card).toHaveStyle(`
15
- background-color: #fff;
16
- `);
16
+ background-color: #fff;
17
+ `);
17
18
  });
18
- it('renders with cover size', () => {
19
- render(<NsFfgCard {...props} additionalProps={{ testid: 'ffg-card-1' }} actionText="Details" />);
20
- const card = screen.getByText('Details');
19
+ it('2. renders with cover size', () => {
20
+ render(<NsFfgCard {...props} additionalProps={{ testid: 'ffg-card-1' }} />);
21
+ const card = screen.getByText(props.cardLink.title);
21
22
  expect(card).toBeInTheDocument();
22
23
  });
24
+ it('3. calls onclick', () => {
25
+ const handler = jest.fn();
26
+ const { getByTestId } = render(
27
+ <NsFfgCard {...props} onClick={handler} additionalProps={{ testid: 'ffg-card-1' }} />
28
+ );
29
+ const card = getByTestId('ffg-card-1');
30
+ fireEvent.click(card);
31
+ expect(handler).toHaveBeenCalled();
32
+ });
23
33
  });
@@ -11,33 +11,63 @@ type MetaStory = Meta<ComponentType>;
11
11
  export default {
12
12
  component: NsFfgCard,
13
13
  title: 'Components/Containers/Card/NsFfgCard',
14
- argTypes: {},
14
+ argTypes: {
15
+ onClick: {
16
+ action: 'clicked',
17
+ },
18
+ },
15
19
  decorators: [withCenterDecorator],
16
20
  } as MetaStory;
17
21
 
22
+ const text = `Every child deserves a chance to shine. Through our Nu Skin Force for Good Foundation,
23
+ we’re empowering children around the world with hope for a brighter future.`;
24
+ const host = 'https://images.contentstack.io/v3/assets';
25
+
18
26
  export const Default: Story = {
19
27
  args: {
20
- actionText: 'LEARN MORE',
21
- actionLink: 'https://www.forceforgood.org/',
22
- // eslint-disable-next-line max-len
23
- text: 'Every child deserves a chance to shine. Through our Nu Skin Force for Good Foundation, we’re empowering children around the world with hope for a brighter future.',
24
- topImageUrl:
25
- // eslint-disable-next-line max-len
26
- 'https://images.contentstack.io/v3/assets/blt594f30e2dc643eae/blt34cdb300dc9b93ff/645ccaa7520ba1a1af13a08a/nsffg-foundation.webp',
28
+ cardLink: {
29
+ href: 'https://www.forceforgood.org/',
30
+ title: 'LEARN MORE',
31
+ },
32
+ text,
33
+ topImageUrl: `${host}/blt594f30e2dc643eae/blt34cdb300dc9b93ff/645ccaa7520ba1a1af13a08a/nsffg-foundation.webp`,
34
+ topImageAlt: 'Force for Good Foundation',
35
+ additionalProps: {
36
+ testid: 'ffg-card',
37
+ },
38
+ onClick: () => {
39
+ console.log('onClick called');
40
+ },
41
+ },
42
+ play: async ({ canvasElement }: any) => {
43
+ const canvas = within(canvasElement);
44
+ userEvent.hover(canvas.getByTestId('ffg-card'));
45
+ userEvent.hover(canvas.getByRole('link'));
46
+ expect(canvas.getByText(text)).toBeInTheDocument();
47
+ },
48
+ };
49
+ const d = { count: -1 };
50
+ export const OnClick: Story = {
51
+ args: {
52
+ cardLink: {
53
+ href: 'https://www.forceforgood.org/',
54
+ title: 'LEARN MORE',
55
+ },
56
+ text,
57
+ topImageUrl: `${host}/blt594f30e2dc643eae/blt34cdb300dc9b93ff/645ccaa7520ba1a1af13a08a/nsffg-foundation.webp`,
27
58
  topImageAlt: 'Force for Good Foundation',
28
59
  additionalProps: {
29
60
  testid: 'ffg-card',
30
61
  },
62
+ onClick: () => {
63
+ d.count = 1;
64
+ console.log('on click called');
65
+ },
31
66
  },
32
- play: async ({ canvasElement }) => {
67
+ play: async ({ canvasElement }: any) => {
68
+ d.count = 0;
33
69
  const canvas = within(canvasElement);
34
- await userEvent.hover(canvas.getByTestId('ffg-card'));
35
- await userEvent.hover(canvas.getByRole('link'));
36
- expect(
37
- canvas.getByText(
38
- // eslint-disable-next-line max-len
39
- 'Every child deserves a chance to shine. Through our Nu Skin Force for Good Foundation, we’re empowering children around the world with hope for a brighter future.'
40
- )
41
- ).toBeInTheDocument();
70
+ userEvent.click(canvas.getByTestId('ffg-card'));
71
+ expect(d.count).toEqual(1);
42
72
  },
43
73
  };
@@ -1,8 +1,6 @@
1
1
  import { Box } from '@mui/material';
2
2
  import { styled } from '@nuskin/foundation-theme';
3
3
 
4
- const options = {};
5
-
6
4
  const Component = () => {
7
5
  return {
8
6
  backgroundClip: 'border-box',
@@ -10,9 +8,10 @@ const Component = () => {
10
8
  border: '1px solid #eff2f7',
11
9
  borderRadius: '1rem',
12
10
  boxShadow: '0 0.75rem 1.5rem rgba(18, 38, 63, .03)',
11
+ cursor: 'pointer',
13
12
  display: 'flex',
14
13
  flexDirection: 'column' as 'column', // https://github.com/cssinjs/jss/issues/1344
15
- marginBottom: '2rem',
14
+ marginBottom: '16px',
16
15
  transition: 'all .2s ease',
17
16
  textAlign: 'center' as 'center',
18
17
  wordWrap: 'break-word' as 'break-word',
@@ -25,12 +24,15 @@ const Component = () => {
25
24
  },
26
25
  },
27
26
  '.body-text': {
28
- padding: '0 1.5rem 3rem',
29
- fontSize: '1rem',
27
+ padding: '0 1.5rem 4rem',
28
+ fontSize: '16px',
30
29
  color: '#8492a6',
31
30
  },
32
31
  '.top-image': {
33
- padding: '1.5rem 3rem',
32
+ padding: '3rem 1.5rem',
33
+ img: {
34
+ maxWidth: '100%',
35
+ },
34
36
  },
35
37
  '.action-link': {
36
38
  color: '#3c4858',
@@ -41,4 +43,4 @@ const Component = () => {
41
43
  },
42
44
  };
43
45
  };
44
- export const NsFfgStyled = styled(Box, options)(Component);
46
+ export const NsFfgStyled = styled(Box)(Component);
@@ -1,9 +1,12 @@
1
- import { AdditionalProps } from '../common.types';
2
-
1
+ import { AdditionalProps, CslpTag } from '../common.types';
3
2
  export type NsFfgCardProps = AdditionalProps & {
4
3
  topImageUrl: string;
5
4
  topImageAlt: string;
6
5
  text: string;
7
- actionLink: string;
8
- actionText?: string;
6
+ cardLink: { href: string; title: string };
7
+ onClick(): void;
8
+ cslp?: {
9
+ card_text: CslpTag;
10
+ action_text: CslpTag;
11
+ };
9
12
  };
@@ -1,8 +1,6 @@
1
1
  import { FoundationTheme } from '@nuskin/foundation-theme';
2
2
 
3
- export type ContentStackLiveTags = {
4
- 'data-cslp'?: string;
5
- };
3
+ export type CslpTag = { 'data-cslp': string };
6
4
 
7
5
  export type AdditionalProps = {
8
6
  additionalProps?: {
@@ -10,7 +8,8 @@ export type AdditionalProps = {
10
8
  testid?: string;
11
9
  className?: string;
12
10
  'data-testid'?: string;
13
- } & ContentStackLiveTags;
11
+ } & Partial<CslpTag>;
12
+ cslp?: Record<string, CslpTag>;
14
13
  } & DarkModeEnabledOption;
15
14
 
16
15
  export type Themed = {
@@ -1,7 +1,7 @@
1
1
  import { ReactNode } from 'react';
2
2
  import { FoundationTheme } from '@nuskin/foundation-theme';
3
3
  import { Variants } from '../typography/types';
4
- import { AdditionalProps, ContentStackLiveTags } from '../common.types';
4
+ import { AdditionalProps, CslpTag } from '../common.types';
5
5
 
6
6
  export type NavTabs = {
7
7
  tabName: string;
@@ -9,7 +9,7 @@ export type NavTabs = {
9
9
  activeColor: FoundationTheme.PaletteColors;
10
10
  color?: FoundationTheme.PaletteColors;
11
11
  textVariant: Variants;
12
- liveTag?: ContentStackLiveTags;
12
+ liveTag?: CslpTag;
13
13
  };
14
14
 
15
15
  export type NSNavTabComponentProps = {