@sproutsocial/seeds-react-content-header 0.2.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.
@@ -0,0 +1,21 @@
1
+ yarn run v1.22.22
2
+ $ tsup --dts
3
+ CLI Building entry: src/index.ts
4
+ CLI Using tsconfig: tsconfig.json
5
+ CLI tsup v8.5.0
6
+ CLI Using tsup config: /home/runner/_work/seeds/seeds/seeds-react/seeds-react-content-header/tsup.config.ts
7
+ CLI Target: es2022
8
+ CLI Cleaning output folder
9
+ CJS Build start
10
+ ESM Build start
11
+ CJS dist/index.js 6.37 KB
12
+ CJS dist/index.js.map 6.79 KB
13
+ CJS ⚡️ Build success in 41ms
14
+ ESM dist/esm/index.js 3.98 KB
15
+ ESM dist/esm/index.js.map 6.77 KB
16
+ ESM ⚡️ Build success in 41ms
17
+ DTS Build start
18
+ DTS ⚡️ Build success in 2119ms
19
+ DTS dist/index.d.ts 1.84 KB
20
+ DTS dist/index.d.mts 1.84 KB
21
+ Done in 2.99s.
package/CHANGELOG.md ADDED
@@ -0,0 +1,20 @@
1
+ # @sproutsocial/seeds-react-content-header
2
+
3
+ ## 0.2.1
4
+
5
+ ### Patch Changes
6
+
7
+ - @sproutsocial/seeds-react-box@1.1.14
8
+ - @sproutsocial/seeds-react-icon@2.2.5
9
+ - @sproutsocial/seeds-react-mixins@4.3.1
10
+
11
+ ## 0.2.0
12
+
13
+ ### Minor Changes
14
+
15
+ - 5bb63e1: New ContentHeader and CollapsibleContentHeader components that extract the shared header-with-actions layout pattern. Re-exported from racine.
16
+
17
+ ### Patch Changes
18
+
19
+ - Updated dependencies [5bb63e1]
20
+ - @sproutsocial/seeds-react-mixins@4.3.0
@@ -0,0 +1,153 @@
1
+ // src/ContentHeader.tsx
2
+ import "react";
3
+ import { Box as Box2 } from "@sproutsocial/seeds-react-box";
4
+
5
+ // src/styles.ts
6
+ import styled from "styled-components";
7
+ import { divider, focusRing } from "@sproutsocial/seeds-react-mixins";
8
+ var HeaderContainer = styled.div`
9
+ box-sizing: border-box;
10
+ display: flex;
11
+ justify-content: space-between;
12
+ align-items: center;
13
+ width: 100%;
14
+ color: ${({ theme }) => theme.colors.text.headline};
15
+ padding: ${({ theme }) => theme.space[400]};
16
+
17
+ ${({ $bordered }) => $bordered && divider}
18
+
19
+ /* Hide divider when collapsible trigger is closed (no content below) */
20
+ &:has(button[data-state="closed"]) {
21
+ border-bottom-color: transparent;
22
+ }
23
+ `;
24
+ var TriggerButton = styled.button`
25
+ all: unset;
26
+ display: flex;
27
+ align-items: center;
28
+ justify-content: space-between;
29
+ flex: 1;
30
+ width: 100%;
31
+ cursor: pointer;
32
+ min-width: 0;
33
+
34
+ .triggerIcon {
35
+ flex-shrink: 0;
36
+ transition: transform 300ms ease-in-out;
37
+ }
38
+
39
+ &[data-state="open"] .triggerIcon {
40
+ transform: rotate(-180deg);
41
+ }
42
+
43
+ &:focus-visible {
44
+ ${focusRing}
45
+ }
46
+ `;
47
+
48
+ // src/TitleArea.tsx
49
+ import "react";
50
+ import { Box } from "@sproutsocial/seeds-react-box";
51
+ import { Text } from "@sproutsocial/seeds-react-text";
52
+ import { jsx, jsxs } from "react/jsx-runtime";
53
+ var TitleArea = ({
54
+ title,
55
+ subtitle,
56
+ headingLevel = "h4",
57
+ subtitleAs,
58
+ leftSlot,
59
+ rightSlot
60
+ }) => /* @__PURE__ */ jsxs(Box, { display: "flex", alignItems: "center", flex: 1, width: "100%", minWidth: 0, children: [
61
+ leftSlot,
62
+ /* @__PURE__ */ jsxs(Box, { flex: 1, children: [
63
+ /* @__PURE__ */ jsx(Text.Headline, { as: headingLevel, children: title }),
64
+ subtitle && /* @__PURE__ */ jsx(Text.Byline, { as: subtitleAs, fontWeight: "normal", children: subtitle })
65
+ ] }),
66
+ rightSlot
67
+ ] });
68
+
69
+ // src/ContentHeader.tsx
70
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
71
+ var ContentHeader = ({
72
+ title,
73
+ subtitle,
74
+ headingLevel = "h4",
75
+ subtitleAs,
76
+ leftSlot,
77
+ rightSlot,
78
+ headerActions,
79
+ bordered = true
80
+ }) => {
81
+ return /* @__PURE__ */ jsxs2(HeaderContainer, { $bordered: bordered, children: [
82
+ /* @__PURE__ */ jsx2(
83
+ TitleArea,
84
+ {
85
+ title,
86
+ subtitle,
87
+ headingLevel,
88
+ subtitleAs,
89
+ leftSlot,
90
+ rightSlot
91
+ }
92
+ ),
93
+ headerActions && /* @__PURE__ */ jsx2(Box2, { children: headerActions })
94
+ ] });
95
+ };
96
+ var ContentHeader_default = ContentHeader;
97
+
98
+ // src/CollapsibleContentHeader.tsx
99
+ import "react";
100
+ import { Box as Box3 } from "@sproutsocial/seeds-react-box";
101
+ import { Icon } from "@sproutsocial/seeds-react-icon";
102
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
103
+ var CollapsibleContentHeader = ({
104
+ title,
105
+ subtitle,
106
+ headingLevel = "h4",
107
+ subtitleAs,
108
+ leftSlot,
109
+ rightSlot,
110
+ headerActions,
111
+ trigger: TriggerWrapper,
112
+ triggerPosition = "right",
113
+ triggerIcon,
114
+ bordered = true
115
+ }) => {
116
+ const defaultIcon = /* @__PURE__ */ jsx3(
117
+ Icon,
118
+ {
119
+ name: "chevron-down-outline",
120
+ className: "triggerIcon",
121
+ "aria-hidden": "true"
122
+ }
123
+ );
124
+ const resolvedIcon = triggerIcon !== void 0 ? triggerIcon : defaultIcon;
125
+ return /* @__PURE__ */ jsxs3(HeaderContainer, { $bordered: bordered, children: [
126
+ /* @__PURE__ */ jsx3(TriggerWrapper, { children: /* @__PURE__ */ jsxs3(TriggerButton, { type: "button", children: [
127
+ triggerPosition === "left" && /* @__PURE__ */ jsx3(Box3, { mr: 300, children: resolvedIcon }),
128
+ /* @__PURE__ */ jsx3(
129
+ TitleArea,
130
+ {
131
+ title,
132
+ subtitle,
133
+ headingLevel,
134
+ subtitleAs,
135
+ leftSlot,
136
+ rightSlot
137
+ }
138
+ ),
139
+ triggerPosition === "right" && resolvedIcon
140
+ ] }) }),
141
+ headerActions && /* @__PURE__ */ jsx3(Box3, { ml: 300, children: headerActions })
142
+ ] });
143
+ };
144
+ var CollapsibleContentHeader_default = CollapsibleContentHeader;
145
+
146
+ // src/index.ts
147
+ var index_default = ContentHeader_default;
148
+ export {
149
+ CollapsibleContentHeader_default as CollapsibleContentHeader,
150
+ ContentHeader_default as ContentHeader,
151
+ index_default as default
152
+ };
153
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/ContentHeader.tsx","../../src/styles.ts","../../src/TitleArea.tsx","../../src/CollapsibleContentHeader.tsx","../../src/index.ts"],"sourcesContent":["import React from \"react\";\nimport { Box } from \"@sproutsocial/seeds-react-box\";\nimport { type TypeContentHeaderProps } from \"./ContentHeaderTypes\";\nimport { HeaderContainer } from \"./styles\";\nimport { TitleArea } from \"./TitleArea\";\n\nexport const ContentHeader = ({\n title,\n subtitle,\n headingLevel = \"h4\",\n subtitleAs,\n leftSlot,\n rightSlot,\n headerActions,\n bordered = true,\n}: TypeContentHeaderProps) => {\n return (\n <HeaderContainer $bordered={bordered}>\n <TitleArea\n title={title}\n subtitle={subtitle}\n headingLevel={headingLevel}\n subtitleAs={subtitleAs}\n leftSlot={leftSlot}\n rightSlot={rightSlot}\n />\n {headerActions && <Box>{headerActions}</Box>}\n </HeaderContainer>\n );\n};\n\nexport default ContentHeader;\n","import styled from \"styled-components\";\nimport { divider, focusRing } from \"@sproutsocial/seeds-react-mixins\";\n\nexport const HeaderContainer = styled.div<{ $bordered?: boolean }>`\n box-sizing: border-box;\n display: flex;\n justify-content: space-between;\n align-items: center;\n width: 100%;\n color: ${({ theme }) => theme.colors.text.headline};\n padding: ${({ theme }) => theme.space[400]};\n\n ${({ $bordered }) => $bordered && divider}\n\n /* Hide divider when collapsible trigger is closed (no content below) */\n &:has(button[data-state=\"closed\"]) {\n border-bottom-color: transparent;\n }\n`;\n\nexport const TriggerButton = styled.button`\n all: unset;\n display: flex;\n align-items: center;\n justify-content: space-between;\n flex: 1;\n width: 100%;\n cursor: pointer;\n min-width: 0;\n\n .triggerIcon {\n flex-shrink: 0;\n transition: transform 300ms ease-in-out;\n }\n\n &[data-state=\"open\"] .triggerIcon {\n transform: rotate(-180deg);\n }\n\n &:focus-visible {\n ${focusRing}\n }\n`;\n","import React from \"react\";\nimport { Box } from \"@sproutsocial/seeds-react-box\";\nimport { Text } from \"@sproutsocial/seeds-react-text\";\nimport { type TypeHeadingLevel } from \"./ContentHeaderTypes\";\n\ninterface TypeTitleAreaProps {\n title: string;\n subtitle?: string;\n headingLevel?: TypeHeadingLevel;\n subtitleAs?: \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"p\";\n leftSlot?: React.ReactNode;\n rightSlot?: React.ReactNode;\n}\n\nexport const TitleArea = ({\n title,\n subtitle,\n headingLevel = \"h4\",\n subtitleAs,\n leftSlot,\n rightSlot,\n}: TypeTitleAreaProps) => (\n <Box display=\"flex\" alignItems=\"center\" flex={1} width=\"100%\" minWidth={0}>\n {leftSlot}\n <Box flex={1}>\n <Text.Headline as={headingLevel}>{title}</Text.Headline>\n {subtitle && (\n <Text.Byline as={subtitleAs} fontWeight=\"normal\">\n {subtitle}\n </Text.Byline>\n )}\n </Box>\n {rightSlot}\n </Box>\n);\n","import React from \"react\";\nimport { Box } from \"@sproutsocial/seeds-react-box\";\nimport { Icon } from \"@sproutsocial/seeds-react-icon\";\nimport { type TypeCollapsibleContentHeaderProps } from \"./ContentHeaderTypes\";\nimport { HeaderContainer, TriggerButton } from \"./styles\";\nimport { TitleArea } from \"./TitleArea\";\n\nexport const CollapsibleContentHeader = ({\n title,\n subtitle,\n headingLevel = \"h4\",\n subtitleAs,\n leftSlot,\n rightSlot,\n headerActions,\n trigger: TriggerWrapper,\n triggerPosition = \"right\",\n triggerIcon,\n bordered = true,\n}: TypeCollapsibleContentHeaderProps) => {\n const defaultIcon = (\n <Icon\n name=\"chevron-down-outline\"\n className=\"triggerIcon\"\n aria-hidden=\"true\"\n />\n );\n\n const resolvedIcon = triggerIcon !== undefined ? triggerIcon : defaultIcon;\n\n return (\n <HeaderContainer $bordered={bordered}>\n <TriggerWrapper>\n <TriggerButton type=\"button\">\n {triggerPosition === \"left\" && <Box mr={300}>{resolvedIcon}</Box>}\n <TitleArea\n title={title}\n subtitle={subtitle}\n headingLevel={headingLevel}\n subtitleAs={subtitleAs}\n leftSlot={leftSlot}\n rightSlot={rightSlot}\n />\n {triggerPosition === \"right\" && resolvedIcon}\n </TriggerButton>\n </TriggerWrapper>\n {headerActions && <Box ml={300}>{headerActions}</Box>}\n </HeaderContainer>\n );\n};\n\nexport default CollapsibleContentHeader;\n","import ContentHeader from \"./ContentHeader\";\nimport CollapsibleContentHeader from \"./CollapsibleContentHeader\";\n\nexport default ContentHeader;\nexport { ContentHeader };\nexport { CollapsibleContentHeader };\nexport * from \"./ContentHeaderTypes\";\n"],"mappings":"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","names":["Box","jsx","jsxs","Box","Box","jsx","jsxs","Box"]}
@@ -0,0 +1,38 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import React from 'react';
3
+
4
+ type TypeHeadingLevel = "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
5
+ interface TypeContentHeaderProps {
6
+ /** The header title text */
7
+ title: string;
8
+ /** Optional subtitle text */
9
+ subtitle?: string;
10
+ /** Semantic heading level for the title. Defaults to "h4" */
11
+ headingLevel?: TypeHeadingLevel;
12
+ /** Semantic element for the subtitle */
13
+ subtitleAs?: "h2" | "h3" | "h4" | "h5" | "p";
14
+ /** Content rendered before the title */
15
+ leftSlot?: React.ReactNode;
16
+ /** Content rendered after the title */
17
+ rightSlot?: React.ReactNode;
18
+ /** Actions rendered to the right of the header */
19
+ headerActions?: React.ReactNode;
20
+ /** Adds a border-bottom divider. Defaults to true */
21
+ bordered?: boolean;
22
+ }
23
+ interface TypeCollapsibleContentHeaderProps extends TypeContentHeaderProps {
24
+ /** Trigger component to wrap the title area (e.g. Collapsible.Trigger) */
25
+ trigger: React.ComponentType<{
26
+ children: React.ReactElement;
27
+ }>;
28
+ /** Position of the collapse chevron icon. Defaults to "right" */
29
+ triggerPosition?: "left" | "right";
30
+ /** Custom trigger icon. Defaults to chevron-down-outline Icon */
31
+ triggerIcon?: React.ReactNode;
32
+ }
33
+
34
+ declare const ContentHeader: ({ title, subtitle, headingLevel, subtitleAs, leftSlot, rightSlot, headerActions, bordered, }: TypeContentHeaderProps) => react_jsx_runtime.JSX.Element;
35
+
36
+ declare const CollapsibleContentHeader: ({ title, subtitle, headingLevel, subtitleAs, leftSlot, rightSlot, headerActions, trigger: TriggerWrapper, triggerPosition, triggerIcon, bordered, }: TypeCollapsibleContentHeaderProps) => react_jsx_runtime.JSX.Element;
37
+
38
+ export { CollapsibleContentHeader, ContentHeader, type TypeCollapsibleContentHeaderProps, type TypeContentHeaderProps, type TypeHeadingLevel, ContentHeader as default };
@@ -0,0 +1,38 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import React from 'react';
3
+
4
+ type TypeHeadingLevel = "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
5
+ interface TypeContentHeaderProps {
6
+ /** The header title text */
7
+ title: string;
8
+ /** Optional subtitle text */
9
+ subtitle?: string;
10
+ /** Semantic heading level for the title. Defaults to "h4" */
11
+ headingLevel?: TypeHeadingLevel;
12
+ /** Semantic element for the subtitle */
13
+ subtitleAs?: "h2" | "h3" | "h4" | "h5" | "p";
14
+ /** Content rendered before the title */
15
+ leftSlot?: React.ReactNode;
16
+ /** Content rendered after the title */
17
+ rightSlot?: React.ReactNode;
18
+ /** Actions rendered to the right of the header */
19
+ headerActions?: React.ReactNode;
20
+ /** Adds a border-bottom divider. Defaults to true */
21
+ bordered?: boolean;
22
+ }
23
+ interface TypeCollapsibleContentHeaderProps extends TypeContentHeaderProps {
24
+ /** Trigger component to wrap the title area (e.g. Collapsible.Trigger) */
25
+ trigger: React.ComponentType<{
26
+ children: React.ReactElement;
27
+ }>;
28
+ /** Position of the collapse chevron icon. Defaults to "right" */
29
+ triggerPosition?: "left" | "right";
30
+ /** Custom trigger icon. Defaults to chevron-down-outline Icon */
31
+ triggerIcon?: React.ReactNode;
32
+ }
33
+
34
+ declare const ContentHeader: ({ title, subtitle, headingLevel, subtitleAs, leftSlot, rightSlot, headerActions, bordered, }: TypeContentHeaderProps) => react_jsx_runtime.JSX.Element;
35
+
36
+ declare const CollapsibleContentHeader: ({ title, subtitle, headingLevel, subtitleAs, leftSlot, rightSlot, headerActions, trigger: TriggerWrapper, triggerPosition, triggerIcon, bordered, }: TypeCollapsibleContentHeaderProps) => react_jsx_runtime.JSX.Element;
37
+
38
+ export { CollapsibleContentHeader, ContentHeader, type TypeCollapsibleContentHeaderProps, type TypeContentHeaderProps, type TypeHeadingLevel, ContentHeader as default };
package/dist/index.js ADDED
@@ -0,0 +1,191 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ CollapsibleContentHeader: () => CollapsibleContentHeader_default,
34
+ ContentHeader: () => ContentHeader_default,
35
+ default: () => index_default
36
+ });
37
+ module.exports = __toCommonJS(index_exports);
38
+
39
+ // src/ContentHeader.tsx
40
+ var import_react2 = require("react");
41
+ var import_seeds_react_box2 = require("@sproutsocial/seeds-react-box");
42
+
43
+ // src/styles.ts
44
+ var import_styled_components = __toESM(require("styled-components"));
45
+ var import_seeds_react_mixins = require("@sproutsocial/seeds-react-mixins");
46
+ var HeaderContainer = import_styled_components.default.div`
47
+ box-sizing: border-box;
48
+ display: flex;
49
+ justify-content: space-between;
50
+ align-items: center;
51
+ width: 100%;
52
+ color: ${({ theme }) => theme.colors.text.headline};
53
+ padding: ${({ theme }) => theme.space[400]};
54
+
55
+ ${({ $bordered }) => $bordered && import_seeds_react_mixins.divider}
56
+
57
+ /* Hide divider when collapsible trigger is closed (no content below) */
58
+ &:has(button[data-state="closed"]) {
59
+ border-bottom-color: transparent;
60
+ }
61
+ `;
62
+ var TriggerButton = import_styled_components.default.button`
63
+ all: unset;
64
+ display: flex;
65
+ align-items: center;
66
+ justify-content: space-between;
67
+ flex: 1;
68
+ width: 100%;
69
+ cursor: pointer;
70
+ min-width: 0;
71
+
72
+ .triggerIcon {
73
+ flex-shrink: 0;
74
+ transition: transform 300ms ease-in-out;
75
+ }
76
+
77
+ &[data-state="open"] .triggerIcon {
78
+ transform: rotate(-180deg);
79
+ }
80
+
81
+ &:focus-visible {
82
+ ${import_seeds_react_mixins.focusRing}
83
+ }
84
+ `;
85
+
86
+ // src/TitleArea.tsx
87
+ var import_react = require("react");
88
+ var import_seeds_react_box = require("@sproutsocial/seeds-react-box");
89
+ var import_seeds_react_text = require("@sproutsocial/seeds-react-text");
90
+ var import_jsx_runtime = require("react/jsx-runtime");
91
+ var TitleArea = ({
92
+ title,
93
+ subtitle,
94
+ headingLevel = "h4",
95
+ subtitleAs,
96
+ leftSlot,
97
+ rightSlot
98
+ }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_seeds_react_box.Box, { display: "flex", alignItems: "center", flex: 1, width: "100%", minWidth: 0, children: [
99
+ leftSlot,
100
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_seeds_react_box.Box, { flex: 1, children: [
101
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_seeds_react_text.Text.Headline, { as: headingLevel, children: title }),
102
+ subtitle && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_seeds_react_text.Text.Byline, { as: subtitleAs, fontWeight: "normal", children: subtitle })
103
+ ] }),
104
+ rightSlot
105
+ ] });
106
+
107
+ // src/ContentHeader.tsx
108
+ var import_jsx_runtime2 = require("react/jsx-runtime");
109
+ var ContentHeader = ({
110
+ title,
111
+ subtitle,
112
+ headingLevel = "h4",
113
+ subtitleAs,
114
+ leftSlot,
115
+ rightSlot,
116
+ headerActions,
117
+ bordered = true
118
+ }) => {
119
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(HeaderContainer, { $bordered: bordered, children: [
120
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
121
+ TitleArea,
122
+ {
123
+ title,
124
+ subtitle,
125
+ headingLevel,
126
+ subtitleAs,
127
+ leftSlot,
128
+ rightSlot
129
+ }
130
+ ),
131
+ headerActions && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_seeds_react_box2.Box, { children: headerActions })
132
+ ] });
133
+ };
134
+ var ContentHeader_default = ContentHeader;
135
+
136
+ // src/CollapsibleContentHeader.tsx
137
+ var import_react3 = require("react");
138
+ var import_seeds_react_box3 = require("@sproutsocial/seeds-react-box");
139
+ var import_seeds_react_icon = require("@sproutsocial/seeds-react-icon");
140
+ var import_jsx_runtime3 = require("react/jsx-runtime");
141
+ var CollapsibleContentHeader = ({
142
+ title,
143
+ subtitle,
144
+ headingLevel = "h4",
145
+ subtitleAs,
146
+ leftSlot,
147
+ rightSlot,
148
+ headerActions,
149
+ trigger: TriggerWrapper,
150
+ triggerPosition = "right",
151
+ triggerIcon,
152
+ bordered = true
153
+ }) => {
154
+ const defaultIcon = /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
155
+ import_seeds_react_icon.Icon,
156
+ {
157
+ name: "chevron-down-outline",
158
+ className: "triggerIcon",
159
+ "aria-hidden": "true"
160
+ }
161
+ );
162
+ const resolvedIcon = triggerIcon !== void 0 ? triggerIcon : defaultIcon;
163
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(HeaderContainer, { $bordered: bordered, children: [
164
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(TriggerWrapper, { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(TriggerButton, { type: "button", children: [
165
+ triggerPosition === "left" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_seeds_react_box3.Box, { mr: 300, children: resolvedIcon }),
166
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
167
+ TitleArea,
168
+ {
169
+ title,
170
+ subtitle,
171
+ headingLevel,
172
+ subtitleAs,
173
+ leftSlot,
174
+ rightSlot
175
+ }
176
+ ),
177
+ triggerPosition === "right" && resolvedIcon
178
+ ] }) }),
179
+ headerActions && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_seeds_react_box3.Box, { ml: 300, children: headerActions })
180
+ ] });
181
+ };
182
+ var CollapsibleContentHeader_default = CollapsibleContentHeader;
183
+
184
+ // src/index.ts
185
+ var index_default = ContentHeader_default;
186
+ // Annotate the CommonJS export names for ESM import in node:
187
+ 0 && (module.exports = {
188
+ CollapsibleContentHeader,
189
+ ContentHeader
190
+ });
191
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/ContentHeader.tsx","../src/styles.ts","../src/TitleArea.tsx","../src/CollapsibleContentHeader.tsx"],"sourcesContent":["import ContentHeader from \"./ContentHeader\";\nimport CollapsibleContentHeader from \"./CollapsibleContentHeader\";\n\nexport default ContentHeader;\nexport { ContentHeader };\nexport { CollapsibleContentHeader };\nexport * from \"./ContentHeaderTypes\";\n","import React from \"react\";\nimport { Box } from \"@sproutsocial/seeds-react-box\";\nimport { type TypeContentHeaderProps } from \"./ContentHeaderTypes\";\nimport { HeaderContainer } from \"./styles\";\nimport { TitleArea } from \"./TitleArea\";\n\nexport const ContentHeader = ({\n title,\n subtitle,\n headingLevel = \"h4\",\n subtitleAs,\n leftSlot,\n rightSlot,\n headerActions,\n bordered = true,\n}: TypeContentHeaderProps) => {\n return (\n <HeaderContainer $bordered={bordered}>\n <TitleArea\n title={title}\n subtitle={subtitle}\n headingLevel={headingLevel}\n subtitleAs={subtitleAs}\n leftSlot={leftSlot}\n rightSlot={rightSlot}\n />\n {headerActions && <Box>{headerActions}</Box>}\n </HeaderContainer>\n );\n};\n\nexport default ContentHeader;\n","import styled from \"styled-components\";\nimport { divider, focusRing } from \"@sproutsocial/seeds-react-mixins\";\n\nexport const HeaderContainer = styled.div<{ $bordered?: boolean }>`\n box-sizing: border-box;\n display: flex;\n justify-content: space-between;\n align-items: center;\n width: 100%;\n color: ${({ theme }) => theme.colors.text.headline};\n padding: ${({ theme }) => theme.space[400]};\n\n ${({ $bordered }) => $bordered && divider}\n\n /* Hide divider when collapsible trigger is closed (no content below) */\n &:has(button[data-state=\"closed\"]) {\n border-bottom-color: transparent;\n }\n`;\n\nexport const TriggerButton = styled.button`\n all: unset;\n display: flex;\n align-items: center;\n justify-content: space-between;\n flex: 1;\n width: 100%;\n cursor: pointer;\n min-width: 0;\n\n .triggerIcon {\n flex-shrink: 0;\n transition: transform 300ms ease-in-out;\n }\n\n &[data-state=\"open\"] .triggerIcon {\n transform: rotate(-180deg);\n }\n\n &:focus-visible {\n ${focusRing}\n }\n`;\n","import React from \"react\";\nimport { Box } from \"@sproutsocial/seeds-react-box\";\nimport { Text } from \"@sproutsocial/seeds-react-text\";\nimport { type TypeHeadingLevel } from \"./ContentHeaderTypes\";\n\ninterface TypeTitleAreaProps {\n title: string;\n subtitle?: string;\n headingLevel?: TypeHeadingLevel;\n subtitleAs?: \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"p\";\n leftSlot?: React.ReactNode;\n rightSlot?: React.ReactNode;\n}\n\nexport const TitleArea = ({\n title,\n subtitle,\n headingLevel = \"h4\",\n subtitleAs,\n leftSlot,\n rightSlot,\n}: TypeTitleAreaProps) => (\n <Box display=\"flex\" alignItems=\"center\" flex={1} width=\"100%\" minWidth={0}>\n {leftSlot}\n <Box flex={1}>\n <Text.Headline as={headingLevel}>{title}</Text.Headline>\n {subtitle && (\n <Text.Byline as={subtitleAs} fontWeight=\"normal\">\n {subtitle}\n </Text.Byline>\n )}\n </Box>\n {rightSlot}\n </Box>\n);\n","import React from \"react\";\nimport { Box } from \"@sproutsocial/seeds-react-box\";\nimport { Icon } from \"@sproutsocial/seeds-react-icon\";\nimport { type TypeCollapsibleContentHeaderProps } from \"./ContentHeaderTypes\";\nimport { HeaderContainer, TriggerButton } from \"./styles\";\nimport { TitleArea } from \"./TitleArea\";\n\nexport const CollapsibleContentHeader = ({\n title,\n subtitle,\n headingLevel = \"h4\",\n subtitleAs,\n leftSlot,\n rightSlot,\n headerActions,\n trigger: TriggerWrapper,\n triggerPosition = \"right\",\n triggerIcon,\n bordered = true,\n}: TypeCollapsibleContentHeaderProps) => {\n const defaultIcon = (\n <Icon\n name=\"chevron-down-outline\"\n className=\"triggerIcon\"\n aria-hidden=\"true\"\n />\n );\n\n const resolvedIcon = triggerIcon !== undefined ? triggerIcon : defaultIcon;\n\n return (\n <HeaderContainer $bordered={bordered}>\n <TriggerWrapper>\n <TriggerButton type=\"button\">\n {triggerPosition === \"left\" && <Box mr={300}>{resolvedIcon}</Box>}\n <TitleArea\n title={title}\n subtitle={subtitle}\n headingLevel={headingLevel}\n subtitleAs={subtitleAs}\n leftSlot={leftSlot}\n rightSlot={rightSlot}\n />\n {triggerPosition === \"right\" && resolvedIcon}\n </TriggerButton>\n </TriggerWrapper>\n {headerActions && <Box ml={300}>{headerActions}</Box>}\n </HeaderContainer>\n );\n};\n\nexport default CollapsibleContentHeader;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAAAA,gBAAkB;AAClB,IAAAC,0BAAoB;;;ACDpB,+BAAmB;AACnB,gCAAmC;AAE5B,IAAM,kBAAkB,yBAAAC,QAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,WAM3B,CAAC,EAAE,MAAM,MAAM,MAAM,OAAO,KAAK,QAAQ;AAAA,aACvC,CAAC,EAAE,MAAM,MAAM,MAAM,MAAM,GAAG,CAAC;AAAA;AAAA,IAExC,CAAC,EAAE,UAAU,MAAM,aAAa,iCAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQpC,IAAM,gBAAgB,yBAAAA,QAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAoB9B,mCAAS;AAAA;AAAA;;;ACxCf,mBAAkB;AAClB,6BAAoB;AACpB,8BAAqB;AAsBjB;AAVG,IAAM,YAAY,CAAC;AAAA,EACxB;AAAA,EACA;AAAA,EACA,eAAe;AAAA,EACf;AAAA,EACA;AAAA,EACA;AACF,MACE,6CAAC,8BAAI,SAAQ,QAAO,YAAW,UAAS,MAAM,GAAG,OAAM,QAAO,UAAU,GACrE;AAAA;AAAA,EACD,6CAAC,8BAAI,MAAM,GACT;AAAA,gDAAC,6BAAK,UAAL,EAAc,IAAI,cAAe,iBAAM;AAAA,IACvC,YACC,4CAAC,6BAAK,QAAL,EAAY,IAAI,YAAY,YAAW,UACrC,oBACH;AAAA,KAEJ;AAAA,EACC;AAAA,GACH;;;AFhBE,IAAAC,sBAAA;AAXG,IAAM,gBAAgB,CAAC;AAAA,EAC5B;AAAA,EACA;AAAA,EACA,eAAe;AAAA,EACf;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AACb,MAA8B;AAC5B,SACE,8CAAC,mBAAgB,WAAW,UAC1B;AAAA;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA;AAAA,IACF;AAAA,IACC,iBAAiB,6CAAC,+BAAK,yBAAc;AAAA,KACxC;AAEJ;AAEA,IAAO,wBAAQ;;;AG/Bf,IAAAC,gBAAkB;AAClB,IAAAC,0BAAoB;AACpB,8BAAqB;AAmBjB,IAAAC,sBAAA;AAdG,IAAM,2BAA2B,CAAC;AAAA,EACvC;AAAA,EACA;AAAA,EACA,eAAe;AAAA,EACf;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAS;AAAA,EACT,kBAAkB;AAAA,EAClB;AAAA,EACA,WAAW;AACb,MAAyC;AACvC,QAAM,cACJ;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,WAAU;AAAA,MACV,eAAY;AAAA;AAAA,EACd;AAGF,QAAM,eAAe,gBAAgB,SAAY,cAAc;AAE/D,SACE,8CAAC,mBAAgB,WAAW,UAC1B;AAAA,iDAAC,kBACC,wDAAC,iBAAc,MAAK,UACjB;AAAA,0BAAoB,UAAU,6CAAC,+BAAI,IAAI,KAAM,wBAAa;AAAA,MAC3D;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA;AAAA,MACF;AAAA,MACC,oBAAoB,WAAW;AAAA,OAClC,GACF;AAAA,IACC,iBAAiB,6CAAC,+BAAI,IAAI,KAAM,yBAAc;AAAA,KACjD;AAEJ;AAEA,IAAO,mCAAQ;;;AJhDf,IAAO,gBAAQ;","names":["import_react","import_seeds_react_box","styled","import_jsx_runtime","import_react","import_seeds_react_box","import_jsx_runtime"]}
package/jest.config.js ADDED
@@ -0,0 +1,9 @@
1
+ const baseConfig = require("@sproutsocial/seeds-testing");
2
+
3
+ /** * @type {import('jest').Config} */
4
+ const config = {
5
+ ...baseConfig,
6
+ displayName: "seeds-react-content-header",
7
+ };
8
+
9
+ module.exports = config;
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@sproutsocial/seeds-react-content-header",
3
+ "version": "0.2.1",
4
+ "description": "Seeds React ContentHeader",
5
+ "author": "Sprout Social, Inc.",
6
+ "license": "MIT",
7
+ "main": "dist/index.js",
8
+ "module": "dist/esm/index.js",
9
+ "types": "dist/index.d.ts",
10
+ "scripts": {
11
+ "build": "tsup --dts",
12
+ "build:debug": "tsup --dts --metafile",
13
+ "dev": "tsup --watch --dts",
14
+ "clean": "rm -rf .turbo dist",
15
+ "clean:modules": "rm -rf node_modules",
16
+ "typecheck": "tsc --noEmit",
17
+ "test": "jest",
18
+ "test:watch": "jest --watch --coverage=false"
19
+ },
20
+ "dependencies": {
21
+ "@sproutsocial/seeds-react-box": "^1.1.14",
22
+ "@sproutsocial/seeds-react-icon": "^2.2.5",
23
+ "@sproutsocial/seeds-react-mixins": "^4.3.1",
24
+ "@sproutsocial/seeds-react-text": "^1.4.0"
25
+ },
26
+ "devDependencies": {
27
+ "@types/react": "^18.0.0",
28
+ "@types/styled-components": "^5.1.26",
29
+ "@sproutsocial/eslint-config-seeds": "*",
30
+ "react": "^18.0.0",
31
+ "styled-components": "^5.2.3",
32
+ "tsup": "^8.3.4",
33
+ "typescript": "^5.6.2",
34
+ "@sproutsocial/seeds-tsconfig": "*",
35
+ "@sproutsocial/seeds-testing": "*",
36
+ "@sproutsocial/seeds-react-testing-library": "*",
37
+ "@sproutsocial/seeds-react-collapsible": "^2.0.0"
38
+ },
39
+ "peerDependencies": {
40
+ "styled-components": "^5.2.3"
41
+ },
42
+ "engines": {
43
+ "node": ">=18"
44
+ }
45
+ }
@@ -0,0 +1,52 @@
1
+ import React from "react";
2
+ import { Box } from "@sproutsocial/seeds-react-box";
3
+ import { Icon } from "@sproutsocial/seeds-react-icon";
4
+ import { type TypeCollapsibleContentHeaderProps } from "./ContentHeaderTypes";
5
+ import { HeaderContainer, TriggerButton } from "./styles";
6
+ import { TitleArea } from "./TitleArea";
7
+
8
+ export const CollapsibleContentHeader = ({
9
+ title,
10
+ subtitle,
11
+ headingLevel = "h4",
12
+ subtitleAs,
13
+ leftSlot,
14
+ rightSlot,
15
+ headerActions,
16
+ trigger: TriggerWrapper,
17
+ triggerPosition = "right",
18
+ triggerIcon,
19
+ bordered = true,
20
+ }: TypeCollapsibleContentHeaderProps) => {
21
+ const defaultIcon = (
22
+ <Icon
23
+ name="chevron-down-outline"
24
+ className="triggerIcon"
25
+ aria-hidden="true"
26
+ />
27
+ );
28
+
29
+ const resolvedIcon = triggerIcon !== undefined ? triggerIcon : defaultIcon;
30
+
31
+ return (
32
+ <HeaderContainer $bordered={bordered}>
33
+ <TriggerWrapper>
34
+ <TriggerButton type="button">
35
+ {triggerPosition === "left" && <Box mr={300}>{resolvedIcon}</Box>}
36
+ <TitleArea
37
+ title={title}
38
+ subtitle={subtitle}
39
+ headingLevel={headingLevel}
40
+ subtitleAs={subtitleAs}
41
+ leftSlot={leftSlot}
42
+ rightSlot={rightSlot}
43
+ />
44
+ {triggerPosition === "right" && resolvedIcon}
45
+ </TriggerButton>
46
+ </TriggerWrapper>
47
+ {headerActions && <Box ml={300}>{headerActions}</Box>}
48
+ </HeaderContainer>
49
+ );
50
+ };
51
+
52
+ export default CollapsibleContentHeader;
@@ -0,0 +1,32 @@
1
+ import React from "react";
2
+ import { Box } from "@sproutsocial/seeds-react-box";
3
+ import { type TypeContentHeaderProps } from "./ContentHeaderTypes";
4
+ import { HeaderContainer } from "./styles";
5
+ import { TitleArea } from "./TitleArea";
6
+
7
+ export const ContentHeader = ({
8
+ title,
9
+ subtitle,
10
+ headingLevel = "h4",
11
+ subtitleAs,
12
+ leftSlot,
13
+ rightSlot,
14
+ headerActions,
15
+ bordered = true,
16
+ }: TypeContentHeaderProps) => {
17
+ return (
18
+ <HeaderContainer $bordered={bordered}>
19
+ <TitleArea
20
+ title={title}
21
+ subtitle={subtitle}
22
+ headingLevel={headingLevel}
23
+ subtitleAs={subtitleAs}
24
+ leftSlot={leftSlot}
25
+ rightSlot={rightSlot}
26
+ />
27
+ {headerActions && <Box>{headerActions}</Box>}
28
+ </HeaderContainer>
29
+ );
30
+ };
31
+
32
+ export default ContentHeader;
@@ -0,0 +1,32 @@
1
+ import type React from "react";
2
+
3
+ export type TypeHeadingLevel = "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
4
+
5
+ export interface TypeContentHeaderProps {
6
+ /** The header title text */
7
+ title: string;
8
+ /** Optional subtitle text */
9
+ subtitle?: string;
10
+ /** Semantic heading level for the title. Defaults to "h4" */
11
+ headingLevel?: TypeHeadingLevel;
12
+ /** Semantic element for the subtitle */
13
+ subtitleAs?: "h2" | "h3" | "h4" | "h5" | "p";
14
+ /** Content rendered before the title */
15
+ leftSlot?: React.ReactNode;
16
+ /** Content rendered after the title */
17
+ rightSlot?: React.ReactNode;
18
+ /** Actions rendered to the right of the header */
19
+ headerActions?: React.ReactNode;
20
+ /** Adds a border-bottom divider. Defaults to true */
21
+ bordered?: boolean;
22
+ }
23
+
24
+ export interface TypeCollapsibleContentHeaderProps
25
+ extends TypeContentHeaderProps {
26
+ /** Trigger component to wrap the title area (e.g. Collapsible.Trigger) */
27
+ trigger: React.ComponentType<{ children: React.ReactElement }>;
28
+ /** Position of the collapse chevron icon. Defaults to "right" */
29
+ triggerPosition?: "left" | "right";
30
+ /** Custom trigger icon. Defaults to chevron-down-outline Icon */
31
+ triggerIcon?: React.ReactNode;
32
+ }
@@ -0,0 +1,35 @@
1
+ import React from "react";
2
+ import { Box } from "@sproutsocial/seeds-react-box";
3
+ import { Text } from "@sproutsocial/seeds-react-text";
4
+ import { type TypeHeadingLevel } from "./ContentHeaderTypes";
5
+
6
+ interface TypeTitleAreaProps {
7
+ title: string;
8
+ subtitle?: string;
9
+ headingLevel?: TypeHeadingLevel;
10
+ subtitleAs?: "h2" | "h3" | "h4" | "h5" | "p";
11
+ leftSlot?: React.ReactNode;
12
+ rightSlot?: React.ReactNode;
13
+ }
14
+
15
+ export const TitleArea = ({
16
+ title,
17
+ subtitle,
18
+ headingLevel = "h4",
19
+ subtitleAs,
20
+ leftSlot,
21
+ rightSlot,
22
+ }: TypeTitleAreaProps) => (
23
+ <Box display="flex" alignItems="center" flex={1} width="100%" minWidth={0}>
24
+ {leftSlot}
25
+ <Box flex={1}>
26
+ <Text.Headline as={headingLevel}>{title}</Text.Headline>
27
+ {subtitle && (
28
+ <Text.Byline as={subtitleAs} fontWeight="normal">
29
+ {subtitle}
30
+ </Text.Byline>
31
+ )}
32
+ </Box>
33
+ {rightSlot}
34
+ </Box>
35
+ );
@@ -0,0 +1,181 @@
1
+ import React from "react";
2
+ import {
3
+ render,
4
+ screen,
5
+ fireEvent,
6
+ } from "@sproutsocial/seeds-react-testing-library";
7
+ import { Collapsible } from "@sproutsocial/seeds-react-collapsible";
8
+ import { ContentHeader } from "../ContentHeader";
9
+ import { CollapsibleContentHeader } from "../CollapsibleContentHeader";
10
+
11
+ describe("ContentHeader", () => {
12
+ test("renders title", () => {
13
+ render(<ContentHeader title="Test Title" />);
14
+ expect(screen.getByText("Test Title")).toBeInTheDocument();
15
+ });
16
+
17
+ test("renders title with correct heading level", () => {
18
+ render(<ContentHeader title="Test Title" headingLevel="h2" />);
19
+ const title = screen.getByText("Test Title");
20
+ expect(title.tagName).toBe("H2");
21
+ });
22
+
23
+ test("defaults heading level to h4", () => {
24
+ render(<ContentHeader title="Test Title" />);
25
+ const title = screen.getByText("Test Title");
26
+ expect(title.tagName).toBe("H4");
27
+ });
28
+
29
+ test("renders subtitle when provided", () => {
30
+ render(<ContentHeader title="Title" subtitle="Subtitle" />);
31
+ expect(screen.getByText("Subtitle")).toBeInTheDocument();
32
+ });
33
+
34
+ test("does not render subtitle when not provided", () => {
35
+ render(<ContentHeader title="Title" />);
36
+ expect(screen.queryByText("Subtitle")).not.toBeInTheDocument();
37
+ });
38
+
39
+ test("renders headerActions", () => {
40
+ render(
41
+ <ContentHeader title="Title" headerActions={<button>Action</button>} />
42
+ );
43
+ expect(screen.getByText("Action")).toBeInTheDocument();
44
+ });
45
+
46
+ test("does not render trigger button", () => {
47
+ render(<ContentHeader title="Title" />);
48
+ expect(screen.queryByRole("button")).not.toBeInTheDocument();
49
+ });
50
+
51
+ test("renders leftSlot and rightSlot", () => {
52
+ render(
53
+ <ContentHeader
54
+ title="Title"
55
+ leftSlot={<span>Left</span>}
56
+ rightSlot={<span>Right</span>}
57
+ />
58
+ );
59
+
60
+ expect(screen.getByText("Left")).toBeInTheDocument();
61
+ expect(screen.getByText("Right")).toBeInTheDocument();
62
+ });
63
+ });
64
+
65
+ describe("CollapsibleContentHeader", () => {
66
+ test("renders trigger button", () => {
67
+ render(
68
+ <Collapsible isOpen={true} onOpenChange={() => {}}>
69
+ <CollapsibleContentHeader title="Title" trigger={Collapsible.Trigger} />
70
+ <Collapsible.Panel>Content</Collapsible.Panel>
71
+ </Collapsible>
72
+ );
73
+
74
+ const trigger = screen.getByRole("button");
75
+ expect(trigger).toBeInTheDocument();
76
+ });
77
+
78
+ test("headerActions do not toggle collapse", () => {
79
+ const onOpenChange = jest.fn();
80
+
81
+ render(
82
+ <Collapsible isOpen={true} onOpenChange={onOpenChange}>
83
+ <CollapsibleContentHeader
84
+ title="Title"
85
+ trigger={Collapsible.Trigger}
86
+ headerActions={<button>Action</button>}
87
+ />
88
+ <Collapsible.Panel>Content</Collapsible.Panel>
89
+ </Collapsible>
90
+ );
91
+
92
+ fireEvent.click(screen.getByText("Action"));
93
+ expect(onOpenChange).not.toHaveBeenCalled();
94
+ });
95
+
96
+ test("trigger click toggles collapse", () => {
97
+ const onOpenChange = jest.fn();
98
+
99
+ render(
100
+ <Collapsible isOpen={true} onOpenChange={onOpenChange}>
101
+ <CollapsibleContentHeader title="Title" trigger={Collapsible.Trigger} />
102
+ <Collapsible.Panel>Content</Collapsible.Panel>
103
+ </Collapsible>
104
+ );
105
+
106
+ const trigger = screen.getByRole("button");
107
+ fireEvent.click(trigger);
108
+ expect(onOpenChange).toHaveBeenCalledWith(false);
109
+ });
110
+
111
+ test("renders title and subtitle", () => {
112
+ render(
113
+ <Collapsible isOpen={true} onOpenChange={() => {}}>
114
+ <CollapsibleContentHeader
115
+ title="Title"
116
+ subtitle="Subtitle"
117
+ trigger={Collapsible.Trigger}
118
+ />
119
+ <Collapsible.Panel>Content</Collapsible.Panel>
120
+ </Collapsible>
121
+ );
122
+
123
+ expect(screen.getByText("Title")).toBeInTheDocument();
124
+ expect(screen.getByText("Subtitle")).toBeInTheDocument();
125
+ });
126
+
127
+ test("renders leftSlot and rightSlot", () => {
128
+ render(
129
+ <Collapsible isOpen={true} onOpenChange={() => {}}>
130
+ <CollapsibleContentHeader
131
+ title="Title"
132
+ trigger={Collapsible.Trigger}
133
+ leftSlot={<span>Left</span>}
134
+ rightSlot={<span>Right</span>}
135
+ />
136
+ <Collapsible.Panel>Content</Collapsible.Panel>
137
+ </Collapsible>
138
+ );
139
+
140
+ expect(screen.getByText("Left")).toBeInTheDocument();
141
+ expect(screen.getByText("Right")).toBeInTheDocument();
142
+ });
143
+
144
+ test("renders custom triggerIcon", () => {
145
+ render(
146
+ <Collapsible isOpen={true} onOpenChange={() => {}}>
147
+ <CollapsibleContentHeader
148
+ title="Title"
149
+ trigger={Collapsible.Trigger}
150
+ triggerIcon={<span data-testid="custom-icon">▼</span>}
151
+ />
152
+ <Collapsible.Panel>Content</Collapsible.Panel>
153
+ </Collapsible>
154
+ );
155
+
156
+ expect(screen.getByTestId("custom-icon")).toBeInTheDocument();
157
+ });
158
+
159
+ test("renders trigger icon on the left when triggerPosition is left", () => {
160
+ render(
161
+ <Collapsible isOpen={true} onOpenChange={() => {}}>
162
+ <CollapsibleContentHeader
163
+ title="Title"
164
+ trigger={Collapsible.Trigger}
165
+ triggerPosition="left"
166
+ />
167
+ <Collapsible.Panel>Content</Collapsible.Panel>
168
+ </Collapsible>
169
+ );
170
+
171
+ const trigger = screen.getByRole("button");
172
+ const title = screen.getByText("Title");
173
+ // Icon should come before the title in the DOM
174
+ const iconElement = trigger.querySelector(".triggerIcon");
175
+ expect(iconElement).toBeInTheDocument();
176
+ expect(
177
+ title.compareDocumentPosition(iconElement as Node) &
178
+ Node.DOCUMENT_POSITION_PRECEDING
179
+ ).toBeTruthy();
180
+ });
181
+ });
package/src/index.ts ADDED
@@ -0,0 +1,7 @@
1
+ import ContentHeader from "./ContentHeader";
2
+ import CollapsibleContentHeader from "./CollapsibleContentHeader";
3
+
4
+ export default ContentHeader;
5
+ export { ContentHeader };
6
+ export { CollapsibleContentHeader };
7
+ export * from "./ContentHeaderTypes";
package/src/styles.ts ADDED
@@ -0,0 +1,43 @@
1
+ import styled from "styled-components";
2
+ import { divider, focusRing } from "@sproutsocial/seeds-react-mixins";
3
+
4
+ export const HeaderContainer = styled.div<{ $bordered?: boolean }>`
5
+ box-sizing: border-box;
6
+ display: flex;
7
+ justify-content: space-between;
8
+ align-items: center;
9
+ width: 100%;
10
+ color: ${({ theme }) => theme.colors.text.headline};
11
+ padding: ${({ theme }) => theme.space[400]};
12
+
13
+ ${({ $bordered }) => $bordered && divider}
14
+
15
+ /* Hide divider when collapsible trigger is closed (no content below) */
16
+ &:has(button[data-state="closed"]) {
17
+ border-bottom-color: transparent;
18
+ }
19
+ `;
20
+
21
+ export const TriggerButton = styled.button`
22
+ all: unset;
23
+ display: flex;
24
+ align-items: center;
25
+ justify-content: space-between;
26
+ flex: 1;
27
+ width: 100%;
28
+ cursor: pointer;
29
+ min-width: 0;
30
+
31
+ .triggerIcon {
32
+ flex-shrink: 0;
33
+ transition: transform 300ms ease-in-out;
34
+ }
35
+
36
+ &[data-state="open"] .triggerIcon {
37
+ transform: rotate(-180deg);
38
+ }
39
+
40
+ &:focus-visible {
41
+ ${focusRing}
42
+ }
43
+ `;
package/tsconfig.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "extends": "@sproutsocial/seeds-tsconfig/bundler/dom/library-monorepo",
3
+ "compilerOptions": {
4
+ "jsx": "react-jsx",
5
+ "module": "esnext"
6
+ },
7
+ "include": ["src/**/*"],
8
+ "exclude": [
9
+ "node_modules",
10
+ "dist",
11
+ "coverage",
12
+ "**/*.stories.tsx",
13
+ "**/*.stories.ts"
14
+ ]
15
+ }
package/tsup.config.ts ADDED
@@ -0,0 +1,12 @@
1
+ import { defineConfig } from "tsup";
2
+
3
+ export default defineConfig((options) => ({
4
+ entry: ["src/index.ts"],
5
+ format: ["cjs", "esm"],
6
+ clean: true,
7
+ legacyOutput: true,
8
+ dts: options.dts,
9
+ external: ["react"],
10
+ sourcemap: true,
11
+ metafile: options.metafile,
12
+ }));