@purpurds/accordion 5.24.3 → 5.25.0

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/dist/styles.css CHANGED
@@ -1 +1 @@
1
- ._purpur-accordion_u887g_1{container:accordion/inline-size}._purpur-accordion_u887g_1 ._purpur-accordion__title_u887g_4{margin-bottom:var(--purpur-spacing-300);color:var(--purpur-color-text-default)}@container accordion (min-width: 600px){._purpur-accordion_u887g_1 ._purpur-accordion__title_u887g_4{margin-bottom:var(--purpur-spacing-400)}}._purpur-accordion--negative_u887g_13{background-color:var(--purpur-color-purple-900)}._purpur-accordion--negative_u887g_13 ._purpur-accordion__title_u887g_4{color:var(--purpur-color-text-default-negative)}._purpur-accordion-item_aukzj_1{overflow:hidden;border-bottom:var(--purpur-border-width-xs) solid var(--purpur-color-border-weak)}._purpur-accordion-item_aukzj_1:nth-of-type(1){border-top:var(--purpur-border-width-xs) solid var(--purpur-color-border-weak)}._purpur-accordion-item__header_aukzj_8{display:flex}._purpur-accordion-item__trigger_aukzj_11{border:none;padding:0;width:100%;text-align:left;font-family:inherit;font-size:inherit;font-weight:inherit;color:inherit;line-height:inherit;background-color:transparent;transition:background-color var(--purpur-motion-duration-150) ease;display:flex;align-items:center;justify-content:space-between;column-gap:var(--purpur-spacing-100);padding:var(--purpur-spacing-200) var(--purpur-spacing-200) var(--purpur-spacing-200) 0;cursor:pointer;outline:none}@container accordion (min-width: 600px){._purpur-accordion-item__trigger_aukzj_11{padding:var(--purpur-spacing-300) var(--purpur-spacing-300) var(--purpur-spacing-300) 0}}@container accordion (min-width: 1024px){._purpur-accordion-item__trigger_aukzj_11{padding:var(--purpur-spacing-400) var(--purpur-spacing-300) var(--purpur-spacing-400) 0}}._purpur-accordion-item__content_aukzj_41{overflow:hidden}._purpur-accordion-item__content_aukzj_41[data-state=open]{animation:_slideDown_aukzj_1 var(--purpur-motion-duration-150) ease-in-out}._purpur-accordion-item__content_aukzj_41[data-state=closed]{animation:_slideUp_aukzj_1 var(--purpur-motion-duration-150) ease-in-out}._purpur-accordion-item__contentText_aukzj_50{padding:var(--purpur-spacing-200) 0;max-width:calc(var(--purpur-spacing-1200) / .16)}._purpur-accordion-item__icon_aukzj_54{flex-shrink:0;height:var(--purpur-spacing-300);width:var(--purpur-spacing-300);transition:transform var(--purpur-motion-duration-150) ease-in-out}._purpur-accordion-item__trigger_aukzj_11[data-state=open]>._purpur-accordion-item__icon_aukzj_54{transform:rotate(180deg)}._purpur-accordion-item_aukzj_1:has(:focus-visible){outline:none;box-shadow:0 0 0 var(--purpur-border-width-sm) var(--purpur-color-border-interactive-focus)}._purpur-accordion-item_aukzj_1 ._purpur-accordion-item__title_aukzj_67{max-width:100%}._purpur-accordion-item_aukzj_1 ._purpur-accordion-item__title_aukzj_67,._purpur-accordion-item__icon_aukzj_54{color:var(--purpur-color-text-interactive-primary)}._purpur-accordion-item__trigger_aukzj_11:hover{background-color:var(--purpur-color-background-interactive-transparent-hover)}._purpur-accordion-item__trigger_aukzj_11:hover ._purpur-accordion-item__title_aukzj_67,._purpur-accordion-item__trigger_aukzj_11:hover__icon{color:var(--purpur-color-text-interactive-primary-hover)}._purpur-accordion-item__trigger_aukzj_11:active{background-color:var(--purpur-color-background-interactive-transparent-active)}._purpur-accordion-item__trigger_aukzj_11:active ._purpur-accordion-item__title_aukzj_67,._purpur-accordion-item__trigger_aukzj_11:active__icon{color:var(--purpur-color-text-interactive-primary-active)}._purpur-accordion-item__contentText_aukzj_50 p{color:var(--purpur-color-text-default)}._purpur-accordion-item--negative_aukzj_88{border-bottom:var(--purpur-border-width-xs) solid var(--purpur-color-border-weak-negative)}._purpur-accordion-item--negative_aukzj_88:nth-of-type(1){border-top:var(--purpur-border-width-xs) solid var(--purpur-color-border-weak-negative)}._purpur-accordion-item--negative_aukzj_88 ._purpur-accordion-item__title_aukzj_67,._purpur-accordion-item--negative_aukzj_88 ._purpur-accordion-item__icon_aukzj_54{color:var(--purpur-color-text-interactive-primary-negative)}._purpur-accordion-item--negative_aukzj_88 ._purpur-accordion-item__trigger_aukzj_11:hover{background-color:var(--purpur-color-background-interactive-transparent-negative-hover)}._purpur-accordion-item--negative_aukzj_88 ._purpur-accordion-item__trigger_aukzj_11:hover ._purpur-accordion-item__title_aukzj_67,._purpur-accordion-item--negative_aukzj_88 ._purpur-accordion-item__trigger_aukzj_11:hover ._purpur-accordion-item__icon_aukzj_54{color:var(--purpur-color-border-interactive-primary-negative-hover)}._purpur-accordion-item--negative_aukzj_88 ._purpur-accordion-item__trigger_aukzj_11:active{background-color:var(--purpur-color-background-interactive-transparent-negative-active)}._purpur-accordion-item--negative_aukzj_88 ._purpur-accordion-item__trigger_aukzj_11:active ._purpur-accordion-item__title_aukzj_67,._purpur-accordion-item--negative_aukzj_88 ._purpur-accordion-item__trigger_aukzj_11:active ._purpur-accordion-item__icon_aukzj_54{color:var(--purpur-color-border-interactive-primary-negative-active)}._purpur-accordion-item--negative_aukzj_88 ._purpur-accordion-item__contentText_aukzj_50 p{color:var(--purpur-color-text-default-negative)}@keyframes _slideDown_aukzj_1{0%{height:0}to{height:var(--radix-accordion-content-height)}}@keyframes _slideUp_aukzj_1{0%{height:var(--radix-accordion-content-height)}to{height:0}}
1
+ ._purpur-accordion_u887g_1{container:accordion/inline-size}._purpur-accordion_u887g_1 ._purpur-accordion__title_u887g_4{margin-bottom:var(--purpur-spacing-300);color:var(--purpur-color-text-default)}@container accordion (min-width: 600px){._purpur-accordion_u887g_1 ._purpur-accordion__title_u887g_4{margin-bottom:var(--purpur-spacing-400)}}._purpur-accordion--negative_u887g_13{background-color:var(--purpur-color-purple-900)}._purpur-accordion--negative_u887g_13 ._purpur-accordion__title_u887g_4{color:var(--purpur-color-text-default-negative)}._purpur-accordion-item_29ds4_1{border-bottom:var(--purpur-border-width-xs) solid var(--purpur-color-border-weak)}._purpur-accordion-item_29ds4_1:has(button:hover){border-bottom-color:transparent}._purpur-accordion-item_29ds4_1:nth-of-type(1){border-top:var(--purpur-border-width-xs) solid var(--purpur-color-border-weak)}._purpur-accordion-item__header_29ds4_10{display:flex}._purpur-accordion-item__trigger_29ds4_13{border:none;padding:0;width:100%;text-align:left;font-family:inherit;font-size:inherit;font-weight:inherit;color:inherit;line-height:inherit;background-color:transparent;transition:background-color var(--purpur-motion-duration-150) ease;display:flex;align-items:center;justify-content:space-between;column-gap:var(--purpur-spacing-100);padding:var(--purpur-spacing-200) var(--purpur-spacing-200) var(--purpur-spacing-200) 0;cursor:pointer;outline:none}@container accordion (min-width: 600px){._purpur-accordion-item__trigger_29ds4_13{padding:var(--purpur-spacing-300) var(--purpur-spacing-300) var(--purpur-spacing-300) 0}}@container accordion (min-width: 1024px){._purpur-accordion-item__trigger_29ds4_13{padding:var(--purpur-spacing-400) var(--purpur-spacing-300) var(--purpur-spacing-400) 0}}._purpur-accordion-item__trigger_29ds4_13[data-state=open]>._purpur-accordion-item__trigger__icon_29ds4_43{transform:rotate(180deg)}._purpur-accordion-item__trigger_29ds4_13:hover{background-color:var(--purpur-color-background-interactive-transparent-hover)}._purpur-accordion-item__trigger_29ds4_13:hover ._purpur-accordion-item__title_29ds4_49,._purpur-accordion-item__trigger_29ds4_13:hover__icon{color:var(--purpur-color-text-interactive-primary-hover)}._purpur-accordion-item__trigger_29ds4_13:active{background-color:var(--purpur-color-background-interactive-transparent-active)}._purpur-accordion-item__trigger_29ds4_13:active ._purpur-accordion-item__title_29ds4_49,._purpur-accordion-item__trigger_29ds4_13:active__icon{color:var(--purpur-color-text-interactive-primary-active)}._purpur-accordion-item__trigger_29ds4_13:focus-visible{outline:none;box-shadow:0 0 0 var(--purpur-border-width-sm) var(--purpur-color-border-interactive-focus);box-sizing:content-box}._purpur-accordion-item__rotate_29ds4_63>._purpur-accordion-item__icon_29ds4_63{transform:rotate(180deg)}._purpur-accordion-item__content_29ds4_66{overflow:hidden;display:grid;grid-template-rows:1fr;padding:var(--purpur-spacing-200) 0;transition:var(--purpur-motion-duration-200) var(--purpur-motion-easing-ease-in-out);transition-property:grid-template-rows,padding}._purpur-accordion-item__content_29ds4_66[aria-hidden=true]{grid-template-rows:0fr;padding:0;visibility:hidden}._purpur-accordion-item__contentText_29ds4_79{overflow:hidden;max-width:calc(var(--purpur-spacing-1200) / .16)}._purpur-accordion-item__icon_29ds4_63{flex-shrink:0;height:var(--purpur-spacing-300);width:var(--purpur-spacing-300);transition:transform var(--purpur-motion-duration-150) ease-in-out}._purpur-accordion-item_29ds4_1 ._purpur-accordion-item__title_29ds4_49{max-width:100%}._purpur-accordion-item_29ds4_1 ._purpur-accordion-item__title_29ds4_49,._purpur-accordion-item__icon_29ds4_63{color:var(--purpur-color-text-interactive-primary)}._purpur-accordion-item__contentText_29ds4_79 p{color:var(--purpur-color-text-default)}._purpur-accordion-item--negative_29ds4_98{border-bottom:var(--purpur-border-width-xs) solid var(--purpur-color-border-weak-negative)}._purpur-accordion-item--negative_29ds4_98:nth-of-type(1){border-top:var(--purpur-border-width-xs) solid var(--purpur-color-border-weak-negative)}._purpur-accordion-item--negative_29ds4_98 ._purpur-accordion-item__title_29ds4_49,._purpur-accordion-item--negative_29ds4_98 ._purpur-accordion-item__icon_29ds4_63{color:var(--purpur-color-text-interactive-primary-negative)}._purpur-accordion-item--negative_29ds4_98 ._purpur-accordion-item__trigger_29ds4_13:hover{background-color:var(--purpur-color-background-interactive-transparent-negative-hover)}._purpur-accordion-item--negative_29ds4_98 ._purpur-accordion-item__trigger_29ds4_13:hover ._purpur-accordion-item__title_29ds4_49,._purpur-accordion-item--negative_29ds4_98 ._purpur-accordion-item__trigger_29ds4_13:hover ._purpur-accordion-item__icon_29ds4_63{color:var(--purpur-color-border-interactive-primary-negative-hover)}._purpur-accordion-item--negative_29ds4_98 ._purpur-accordion-item__trigger_29ds4_13:active{background-color:var(--purpur-color-background-interactive-transparent-negative-active)}._purpur-accordion-item--negative_29ds4_98 ._purpur-accordion-item__trigger_29ds4_13:active ._purpur-accordion-item__title_29ds4_49,._purpur-accordion-item--negative_29ds4_98 ._purpur-accordion-item__trigger_29ds4_13:active ._purpur-accordion-item__icon_29ds4_63{color:var(--purpur-color-border-interactive-primary-negative-active)}._purpur-accordion-item--negative_29ds4_98 ._purpur-accordion-item__contentText_29ds4_79 p{color:var(--purpur-color-text-default-negative)}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@purpurds/accordion",
3
- "version": "5.24.3",
3
+ "version": "5.25.0",
4
4
  "license": "AGPL-3.0-only",
5
5
  "main": "./dist/accordion.cjs.js",
6
6
  "types": "./dist/accordion.d.ts",
@@ -15,11 +15,10 @@
15
15
  "source": "src/accordion.tsx",
16
16
  "dependencies": {
17
17
  "classnames": "~2.5.0",
18
- "@radix-ui/react-accordion": "~1.1.2",
19
- "@purpurds/tokens": "5.24.3",
20
- "@purpurds/heading": "5.24.3",
21
- "@purpurds/paragraph": "5.24.3",
22
- "@purpurds/icon": "5.24.3"
18
+ "@purpurds/tokens": "5.25.0",
19
+ "@purpurds/heading": "5.25.0",
20
+ "@purpurds/icon": "5.25.0",
21
+ "@purpurds/paragraph": "5.25.0"
23
22
  },
24
23
  "devDependencies": {
25
24
  "@rushstack/eslint-patch": "~1.10.0",
@@ -2,9 +2,11 @@
2
2
 
3
3
  .purpur-accordion-item {
4
4
  $root: &;
5
- overflow: hidden;
6
5
  border-bottom: var(--purpur-border-width-xs) solid var(--purpur-color-border-weak);
7
6
 
7
+ &:has(button:hover) {
8
+ border-bottom-color: transparent;
9
+ }
8
10
  &:nth-of-type(1) {
9
11
  border-top: var(--purpur-border-width-xs) solid var(--purpur-color-border-weak);
10
12
  }
@@ -40,22 +42,56 @@
40
42
  @container accordion (min-width: #{$purpur-breakpoint-lg}) {
41
43
  padding: var(--purpur-spacing-400) var(--purpur-spacing-300) var(--purpur-spacing-400) 0;
42
44
  }
43
- }
44
45
 
45
- &__content {
46
- overflow: hidden;
46
+ &[data-state="open"] > &__icon {
47
+ transform: rotate(180deg);
48
+ }
49
+ &:hover {
50
+ background-color: var(--purpur-color-background-interactive-transparent-hover);
51
+
52
+ #{$root}__title,
53
+ &__icon {
54
+ color: var(--purpur-color-text-interactive-primary-hover);
55
+ }
56
+ }
57
+
58
+ &:active {
59
+ background-color: var(--purpur-color-background-interactive-transparent-active);
60
+
61
+ #{$root}__title,
62
+ &__icon {
63
+ color: var(--purpur-color-text-interactive-primary-active);
64
+ }
65
+ }
66
+ &:focus-visible {
67
+ outline: none;
68
+ box-shadow: 0 0 0 var(--purpur-border-width-sm) var(--purpur-color-border-interactive-focus);
69
+ box-sizing: content-box;
70
+ }
47
71
  }
48
72
 
49
- &__content[data-state="open"] {
50
- animation: slideDown var(--purpur-motion-duration-150) ease-in-out;
73
+ &__rotate > &__icon {
74
+ transform: rotate(180deg);
51
75
  }
52
76
 
53
- &__content[data-state="closed"] {
54
- animation: slideUp var(--purpur-motion-duration-150) ease-in-out;
77
+ &__content {
78
+ overflow: hidden;
79
+ display: grid;
80
+ grid-template-rows: 1fr;
81
+ padding: var(--purpur-spacing-200) 0;
82
+ transition: var(--purpur-motion-duration-200) var(--purpur-motion-easing-ease-in-out);
83
+ // Needs to be after transition otherwise it will overwrite it
84
+ transition-property: grid-template-rows, padding;
85
+
86
+ &[aria-hidden="true"] {
87
+ grid-template-rows: 0fr;
88
+ padding: 0;
89
+ visibility: hidden;
90
+ }
55
91
  }
56
92
 
57
93
  &__contentText {
58
- padding: var(--purpur-spacing-200) 0;
94
+ overflow: hidden;
59
95
  max-width: calc(var(--purpur-spacing-1200) / 0.16);
60
96
  }
61
97
 
@@ -66,15 +102,6 @@
66
102
  transition: transform var(--purpur-motion-duration-150) ease-in-out;
67
103
  }
68
104
 
69
- &__trigger[data-state="open"] > &__icon {
70
- transform: rotate(180deg);
71
- }
72
-
73
- &:has(:focus-visible) {
74
- outline: none;
75
- box-shadow: 0 0 0 var(--purpur-border-width-sm) var(--purpur-color-border-interactive-focus);
76
- }
77
-
78
105
  #{$root}__title {
79
106
  max-width: 100%;
80
107
  }
@@ -84,24 +111,6 @@
84
111
  color: var(--purpur-color-text-interactive-primary);
85
112
  }
86
113
 
87
- &__trigger:hover {
88
- background-color: var(--purpur-color-background-interactive-transparent-hover);
89
-
90
- #{$root}__title,
91
- &__icon {
92
- color: var(--purpur-color-text-interactive-primary-hover);
93
- }
94
- }
95
-
96
- &__trigger:active {
97
- background-color: var(--purpur-color-background-interactive-transparent-active);
98
-
99
- #{$root}__title,
100
- &__icon {
101
- color: var(--purpur-color-text-interactive-primary-active);
102
- }
103
- }
104
-
105
114
  &__contentText p {
106
115
  color: var(--purpur-color-text-default);
107
116
  }
@@ -141,23 +150,3 @@
141
150
  }
142
151
  }
143
152
  }
144
-
145
- @keyframes slideDown {
146
- from {
147
- height: 0;
148
- }
149
-
150
- to {
151
- height: var(--radix-accordion-content-height);
152
- }
153
- }
154
-
155
- @keyframes slideUp {
156
- from {
157
- height: var(--radix-accordion-content-height);
158
- }
159
-
160
- to {
161
- height: 0;
162
- }
163
- }
@@ -1,12 +1,12 @@
1
- import type { ForwardedRef, ReactNode } from "react";
2
- import React, { forwardRef } from "react";
1
+ import type { ReactNode } from "react";
2
+ import React, { useContext, useId, useState } from "react";
3
3
  import type { HeadingTagType } from "@purpurds/heading";
4
4
  import { Heading } from "@purpurds/heading";
5
5
  import { IconChevronDown } from "@purpurds/icon";
6
6
  import { Paragraph } from "@purpurds/paragraph";
7
- import * as RadixAccordion from "@radix-ui/react-accordion";
8
7
  import c from "classnames/bind";
9
8
 
9
+ import { NegativeContext } from "./accordion";
10
10
  import styles from "./accordion-item.module.scss";
11
11
 
12
12
  const cx = c.bind(styles);
@@ -21,13 +21,10 @@ export type AccordionItemProps = {
21
21
  ["data-testid"]?: string;
22
22
  children: ReactNode;
23
23
  /**
24
- * Different accordion styling
25
- * */
24
+ * @deprecated
25
+ */
26
26
  negative?: boolean;
27
27
  className?: string;
28
- /**
29
- * Title of the accordion item
30
- * */
31
28
  title: string;
32
29
  /**
33
30
  * This is the heading tag used for the accordion item title. Defaults to h3.
@@ -35,9 +32,13 @@ export type AccordionItemProps = {
35
32
  */
36
33
  titleTag?: HeadingTagType;
37
34
  /**
38
- * Value is used to identify the accordion. By default title is used for that purpose. If there are identical titles in the accordion, then value should be defined.
39
- * */
35
+ * @deprecated
36
+ */
40
37
  value?: string;
38
+ /**
39
+ * Makes it possible to set the initial state to opened.
40
+ * */
41
+ initialOpen?: boolean;
41
42
  /**
42
43
  * onClick is called when accordion item is expanded or collapsed
43
44
  * event.currentTarget.dataset.state will be either "open" or "closed"
@@ -45,51 +46,67 @@ export type AccordionItemProps = {
45
46
  onClick?: (event: AccordionItemOnClickEvent) => void;
46
47
  };
47
48
 
48
- export const AccordionItem = forwardRef(
49
- (
49
+ export const AccordionItem = ({
50
+ children,
51
+ className,
52
+ title,
53
+ titleTag = "h3",
54
+ initialOpen = false,
55
+ onClick,
56
+ ...props
57
+ }: AccordionItemProps) => {
58
+ const negative = useContext(NegativeContext);
59
+ const uid = useId();
60
+ const [hidden, setHidden] = useState(!initialOpen);
61
+
62
+ const classes = cx([
63
+ className,
64
+ rootClassName,
50
65
  {
51
- children,
52
- className,
53
- title,
54
- titleTag = "h3",
55
- negative,
56
- onClick,
57
- ...props
58
- }: AccordionItemProps,
59
- ref: ForwardedRef<HTMLButtonElement>
60
- ) => {
61
- const classes = cx([
62
- className,
63
- rootClassName,
64
- {
65
- [`${rootClassName}--negative`]: negative,
66
- },
67
- ]);
66
+ [`${rootClassName}--negative`]: negative,
67
+ },
68
+ ]);
68
69
 
69
- return (
70
- <RadixAccordion.Item className={classes} value={title} {...props}>
71
- <RadixAccordion.Header className={cx(`${rootClassName}__header`)} asChild>
72
- <Heading tag={titleTag} variant="title-100" className={cx(`${rootClassName}__title`)}>
73
- <RadixAccordion.Trigger
74
- ref={ref}
75
- onClick={onClick}
76
- className={cx(`${rootClassName}__trigger`, className)}
77
- >
78
- {title}
79
- <IconChevronDown size="md" className={cx(`${rootClassName}__icon`)} aria-hidden />
80
- </RadixAccordion.Trigger>
81
- </Heading>
82
- </RadixAccordion.Header>
83
- <RadixAccordion.Content className={cx(`${rootClassName}__content`, className)}>
84
- <div className={cx(`${rootClassName}__contentText`)}>
85
- {typeof children === "string" ? (
86
- <Paragraph variant="paragraph-200">{children}</Paragraph>
87
- ) : (
88
- children
89
- )}
90
- </div>
91
- </RadixAccordion.Content>
92
- </RadixAccordion.Item>
93
- );
94
- }
95
- );
70
+ return (
71
+ <div className={classes} {...props}>
72
+ <Heading
73
+ tag={titleTag}
74
+ variant="title-100"
75
+ className={cx([`${rootClassName}__header`, `${rootClassName}__title`])}
76
+ >
77
+ <button
78
+ type="button"
79
+ data-state={hidden ? "open" : "close"} // The state is actually set in the onClick, so reversing the data- output state here
80
+ onClick={(e: AccordionItemOnClickEvent) => {
81
+ onClick && onClick(e);
82
+ setHidden(!hidden);
83
+ }}
84
+ className={cx(`${rootClassName}__trigger`, className, {
85
+ [`${rootClassName}__rotate`]: !hidden,
86
+ })}
87
+ aria-expanded={!hidden}
88
+ aria-controls={uid}
89
+ id={uid + "button"}
90
+ >
91
+ {title}
92
+ <IconChevronDown size="md" className={cx(`${rootClassName}__icon`)} aria-hidden />
93
+ </button>
94
+ </Heading>
95
+ <div
96
+ className={cx(`${rootClassName}__content`, className)}
97
+ aria-hidden={hidden}
98
+ id={uid}
99
+ aria-labelledby={uid + "button"}
100
+ role="region"
101
+ >
102
+ <div className={cx(`${rootClassName}__contentText`)}>
103
+ {typeof children === "string" ? (
104
+ <Paragraph variant="paragraph-200">{children}</Paragraph>
105
+ ) : (
106
+ children
107
+ )}
108
+ </div>
109
+ </div>
110
+ </div>
111
+ );
112
+ };
@@ -44,34 +44,56 @@ const meta = {
44
44
  },
45
45
  },
46
46
  },
47
+ args: {
48
+ title: "Title goes here",
49
+ titleVariant: "title-300",
50
+ defaultValue: ["1"],
51
+ negative: false,
52
+ },
47
53
  } satisfies Meta<typeof Accordion>;
48
54
 
49
55
  export default meta;
50
56
  type Story = StoryObj<typeof Accordion>;
51
57
 
58
+ const text =
59
+ "Place body text here. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus at rutrum nulla";
60
+
61
+ const items = Array(3).fill(text);
62
+
52
63
  export const Showcase: Story = {
53
64
  render: (args) => (
54
65
  <Accordion {...args}>
55
- <Accordion.Item title="Section title" value="1">
56
- Place body text here. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus at
57
- rutrum nulla.
58
- </Accordion.Item>
59
-
60
- <Accordion.Item title="Section title" value="2">
61
- Place body text here. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus at
62
- rutrum nulla.
63
- </Accordion.Item>
66
+ {items.map((t, i) => (
67
+ <Accordion.Item title={`Section title ${i + 1}`} key={i}>
68
+ {t}
69
+ </Accordion.Item>
70
+ ))}
71
+ </Accordion>
72
+ ),
73
+ };
64
74
 
65
- <Accordion.Item title="Section title" value="3">
66
- Place body text here. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus at
67
- rutrum nulla.
68
- </Accordion.Item>
75
+ export const UsingSchema: Story = {
76
+ render: (args) => (
77
+ <Accordion {...args}>
78
+ {items.map((t, i) => (
79
+ <Accordion.Item title={`Section title ${i + 1}`} key={i}>
80
+ {t}
81
+ </Accordion.Item>
82
+ ))}
69
83
  </Accordion>
70
84
  ),
71
85
  args: {
72
- title: "Title goes here",
73
- titleVariant: "title-300",
74
- defaultValue: ["1"],
75
- negative: false,
86
+ schema: JSON.stringify({
87
+ "@context": "https://schema.org",
88
+ "@type": "FAQPage",
89
+ mainEntity: items.map((question) => ({
90
+ "@type": "Question",
91
+ name: question.title,
92
+ acceptedAnswer: {
93
+ "@type": "Answer",
94
+ text: question,
95
+ },
96
+ })),
97
+ }),
76
98
  },
77
99
  };
@@ -37,10 +37,10 @@ describe("Accordion", () => {
37
37
  expect(screen.getByRole("heading", { level: 3, name: "Test heading" })).toBeDefined();
38
38
  });
39
39
 
40
- it("should not render accordion item content when trigger is not clicked", () => {
40
+ it("should render accordion item content when trigger is not clicked", () => {
41
41
  setup();
42
42
 
43
- expect(screen.queryByText("item content")).toBeNull();
43
+ expect(screen.queryByText("item content")).not.toBeNull();
44
44
  });
45
45
 
46
46
  it("should show/hide accordion item content when trigger is clicked", () => {
@@ -52,7 +52,7 @@ describe("Accordion", () => {
52
52
  expect(screen.getByText("item content")).toBeDefined();
53
53
 
54
54
  fireEvent.click(trigger);
55
- expect(screen.queryByText("item content")).toBeNull();
55
+ expect(screen.queryByText("item content")).not.toBeNull();
56
56
  });
57
57
 
58
58
  it("should render custom accordion item content", () => {
@@ -69,18 +69,4 @@ describe("Accordion", () => {
69
69
 
70
70
  expect(screen.getByRole("link", { name: "Test link" })).toBeDefined();
71
71
  });
72
-
73
- it("should send onClick event when trigger is clicked", () => {
74
- setup();
75
-
76
- const trigger = screen.getByRole("button");
77
-
78
- fireEvent.click(trigger);
79
- expect(onClickFunction).toHaveBeenCalled();
80
- expect(onClickFunction.mock.calls[0][0].target.dataset.state).toBe("open");
81
-
82
- fireEvent.click(trigger);
83
- expect(onClickFunction).toHaveBeenCalled();
84
- expect(onClickFunction.mock.calls[0][0].target.dataset.state).toBe("closed");
85
- });
86
72
  });
package/src/accordion.tsx CHANGED
@@ -1,12 +1,5 @@
1
- import React, {
2
- Children,
3
- cloneElement,
4
- JSXElementConstructor,
5
- ReactElement,
6
- ReactNode,
7
- } from "react";
1
+ import React, { createContext, ReactNode } from "react";
8
2
  import { Heading, HeadingTagType, TitleVariantType } from "@purpurds/heading";
9
- import * as RadixAccordion from "@radix-ui/react-accordion";
10
3
  import c from "classnames/bind";
11
4
 
12
5
  import styles from "./accordion.module.scss";
@@ -20,15 +13,23 @@ export type AccordionProps = {
20
13
  children: ReactNode;
21
14
  className?: string;
22
15
  /**
23
- * The values of the accordion items that should be open by default
16
+ * @deprecated
24
17
  * */
25
18
  defaultValue?: string[];
26
19
  negative?: boolean;
27
20
  title?: string;
28
21
  titleTag?: HeadingTagType;
29
22
  titleVariant?: TitleVariantType;
23
+ /**
24
+ * A schema enables external applications to parse the content in a structured manner.
25
+ * See more at schema.org . We use a JSON-LD based script-tag to add the schema to the DOM.
26
+ * Note that only one type of schema can be added per page
27
+ */
28
+ schema?: string;
30
29
  };
31
30
 
31
+ export const NegativeContext = createContext(false);
32
+
32
33
  export const Accordion = ({
33
34
  ["data-testid"]: dataTestId,
34
35
  children,
@@ -37,6 +38,7 @@ export const Accordion = ({
37
38
  title,
38
39
  titleTag = "h2",
39
40
  titleVariant = "title-300",
41
+ schema,
40
42
  ...props
41
43
  }: AccordionProps) => {
42
44
  const classes = cx([
@@ -47,23 +49,24 @@ export const Accordion = ({
47
49
  },
48
50
  ]);
49
51
 
50
- const renderChildren = () =>
51
- Children.map(children, (child) => {
52
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
53
- return cloneElement(child as ReactElement<any, string | JSXElementConstructor<any>>, {
54
- negative,
55
- });
56
- });
57
-
58
52
  return (
59
- <RadixAccordion.Root className={classes} type="multiple" data-testid={dataTestId} {...props}>
60
- {title ? (
53
+ <div className={classes} data-testid={dataTestId} {...props}>
54
+ {title && (
61
55
  <Heading tag={titleTag} variant={titleVariant} className={cx(`${rootClassName}__title`)}>
62
56
  {title}
63
57
  </Heading>
64
- ) : null}
65
- {renderChildren()}
66
- </RadixAccordion.Root>
58
+ )}
59
+ <NegativeContext.Provider value={negative}>{children}</NegativeContext.Provider>
60
+ {schema && (
61
+ <script
62
+ type="application/ld+json"
63
+ // eslint-disable-next-line react/no-danger
64
+ dangerouslySetInnerHTML={{
65
+ __html: schema,
66
+ }}
67
+ />
68
+ )}
69
+ </div>
67
70
  );
68
71
  };
69
72