@sproutsocial/seeds-react-content-block 0.4.1 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +9 -9
- package/CHANGELOG.md +18 -0
- package/dist/esm/index.js +82 -10
- package/dist/esm/index.js.map +1 -1
- package/dist/index.d.mts +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +92 -10
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/ContentBlock.stories.tsx +65 -0
- package/src/ContentBlock.tsx +62 -6
- package/src/__tests__/content-block.test.tsx +59 -0
- package/src/styles.ts +13 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -8,14 +8,14 @@ $ tsup --dts
|
|
|
8
8
|
[34mCLI[39m Cleaning output folder
|
|
9
9
|
[34mCJS[39m Build start
|
|
10
10
|
[34mESM[39m Build start
|
|
11
|
-
[32mCJS[39m [1mdist/index.js [22m[
|
|
12
|
-
[32mCJS[39m [1mdist/index.js.map [22m[
|
|
11
|
+
[32mCJS[39m [1mdist/index.js [22m[32m6.84 KB[39m
|
|
12
|
+
[32mCJS[39m [1mdist/index.js.map [22m[32m8.58 KB[39m
|
|
13
13
|
[32mCJS[39m ⚡️ Build success in 13ms
|
|
14
|
-
[32mESM[39m [1mdist/esm/index.js [22m[
|
|
15
|
-
[32mESM[39m [1mdist/esm/index.js.map [22m[
|
|
16
|
-
[32mESM[39m ⚡️ Build success in
|
|
14
|
+
[32mESM[39m [1mdist/esm/index.js [22m[32m4.37 KB[39m
|
|
15
|
+
[32mESM[39m [1mdist/esm/index.js.map [22m[32m8.56 KB[39m
|
|
16
|
+
[32mESM[39m ⚡️ Build success in 13ms
|
|
17
17
|
[34mDTS[39m Build start
|
|
18
|
-
[32mDTS[39m ⚡️ Build success in
|
|
19
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[32m1.
|
|
20
|
-
[32mDTS[39m [1mdist/index.d.mts [22m[32m1.
|
|
21
|
-
Done in 2.
|
|
18
|
+
[32mDTS[39m ⚡️ Build success in 2084ms
|
|
19
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m1.51 KB[39m
|
|
20
|
+
[32mDTS[39m [1mdist/index.d.mts [22m[32m1.51 KB[39m
|
|
21
|
+
Done in 2.84s.
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @sproutsocial/seeds-react-content-block
|
|
2
2
|
|
|
3
|
+
## 0.5.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- @sproutsocial/seeds-react-button@2.0.3
|
|
8
|
+
- @sproutsocial/seeds-react-content-header@0.2.9
|
|
9
|
+
|
|
10
|
+
## 0.5.0
|
|
11
|
+
|
|
12
|
+
### Minor Changes
|
|
13
|
+
|
|
14
|
+
- eb6937d: Add optional `footerContent` and `footerActions` props for a styled footer area below the content, and `flushLayout` boolean prop to set content area padding to 0
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- Updated dependencies [eb6937d]
|
|
19
|
+
- @sproutsocial/seeds-react-content-header@0.2.8
|
|
20
|
+
|
|
3
21
|
## 0.4.1
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
package/dist/esm/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/ContentBlock.tsx
|
|
2
2
|
import { useState } from "react";
|
|
3
|
-
import { Box } from "@sproutsocial/seeds-react-box";
|
|
3
|
+
import { Box as Box2 } from "@sproutsocial/seeds-react-box";
|
|
4
4
|
import { Loader } from "@sproutsocial/seeds-react-loader";
|
|
5
5
|
import { Collapsible } from "@sproutsocial/seeds-react-collapsible";
|
|
6
6
|
import {
|
|
@@ -8,21 +8,51 @@ import {
|
|
|
8
8
|
CollapsibleContentHeader
|
|
9
9
|
} from "@sproutsocial/seeds-react-content-header";
|
|
10
10
|
import { Container } from "@sproutsocial/seeds-react-container";
|
|
11
|
+
|
|
12
|
+
// src/styles.ts
|
|
13
|
+
import styled from "styled-components";
|
|
14
|
+
import { Box } from "@sproutsocial/seeds-react-box";
|
|
15
|
+
var ContentBlockContent = styled(Box)`
|
|
16
|
+
padding: ${({ theme }) => theme.space[400]};
|
|
17
|
+
`;
|
|
18
|
+
var FooterContainer = styled.div`
|
|
19
|
+
box-sizing: border-box;
|
|
20
|
+
display: flex;
|
|
21
|
+
justify-content: space-between;
|
|
22
|
+
align-items: center;
|
|
23
|
+
width: 100%;
|
|
24
|
+
padding: ${({ theme }) => theme.space[400]};
|
|
25
|
+
border-top: ${({ theme }) => theme.borderWidths[500]} solid
|
|
26
|
+
${({ theme }) => theme.colors.container.border.base};
|
|
27
|
+
border-bottom-left-radius: ${({ theme }) => theme.radii.outer};
|
|
28
|
+
border-bottom-right-radius: ${({ theme }) => theme.radii.outer};
|
|
29
|
+
`;
|
|
30
|
+
|
|
31
|
+
// src/ContentBlock.tsx
|
|
11
32
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
12
33
|
var ContentArea = ({
|
|
13
34
|
isLoading,
|
|
14
35
|
children,
|
|
15
|
-
contentProps
|
|
36
|
+
contentProps,
|
|
37
|
+
flushLayout,
|
|
38
|
+
hasFooter
|
|
16
39
|
}) => /* @__PURE__ */ jsx(
|
|
17
|
-
|
|
40
|
+
Box2,
|
|
18
41
|
{
|
|
19
|
-
p: 400,
|
|
42
|
+
p: flushLayout ? 0 : 400,
|
|
20
43
|
...contentProps,
|
|
21
|
-
borderBottomLeftRadius: "outer",
|
|
22
|
-
borderBottomRightRadius: "outer",
|
|
23
|
-
children: isLoading ? /* @__PURE__ */ jsx(
|
|
44
|
+
borderBottomLeftRadius: hasFooter ? void 0 : "outer",
|
|
45
|
+
borderBottomRightRadius: hasFooter ? void 0 : "outer",
|
|
46
|
+
children: isLoading ? /* @__PURE__ */ jsx(Box2, { my: 400, children: /* @__PURE__ */ jsx(Loader, { delay: false }) }) : children
|
|
24
47
|
}
|
|
25
48
|
);
|
|
49
|
+
var ContentFooter = ({
|
|
50
|
+
footerContent,
|
|
51
|
+
footerActions
|
|
52
|
+
}) => /* @__PURE__ */ jsxs(FooterContainer, { children: [
|
|
53
|
+
footerContent && /* @__PURE__ */ jsx("span", { children: footerContent }),
|
|
54
|
+
footerActions && /* @__PURE__ */ jsx("span", { children: footerActions })
|
|
55
|
+
] });
|
|
26
56
|
var CollapsibleContentBlock = ({
|
|
27
57
|
title,
|
|
28
58
|
subtitle,
|
|
@@ -32,12 +62,16 @@ var CollapsibleContentBlock = ({
|
|
|
32
62
|
isLoading,
|
|
33
63
|
headerActions,
|
|
34
64
|
contentProps,
|
|
65
|
+
flushLayout,
|
|
66
|
+
footerContent,
|
|
67
|
+
footerActions,
|
|
35
68
|
isOpen: controlledIsOpen,
|
|
36
69
|
defaultOpen = true,
|
|
37
70
|
onOpenChange
|
|
38
71
|
}) => {
|
|
39
72
|
const [internalOpen, setInternalOpen] = useState(defaultOpen);
|
|
40
73
|
const open = controlledIsOpen !== void 0 ? controlledIsOpen : internalOpen;
|
|
74
|
+
const hasFooter = !!(footerContent || footerActions);
|
|
41
75
|
const handleOpenChange = (newOpen) => {
|
|
42
76
|
if (controlledIsOpen === void 0) {
|
|
43
77
|
setInternalOpen(newOpen);
|
|
@@ -57,7 +91,25 @@ var CollapsibleContentBlock = ({
|
|
|
57
91
|
triggerPosition: "left"
|
|
58
92
|
}
|
|
59
93
|
),
|
|
60
|
-
/* @__PURE__ */
|
|
94
|
+
/* @__PURE__ */ jsxs(Collapsible.Panel, { children: [
|
|
95
|
+
/* @__PURE__ */ jsx(
|
|
96
|
+
ContentArea,
|
|
97
|
+
{
|
|
98
|
+
isLoading,
|
|
99
|
+
contentProps,
|
|
100
|
+
flushLayout,
|
|
101
|
+
hasFooter,
|
|
102
|
+
children
|
|
103
|
+
}
|
|
104
|
+
),
|
|
105
|
+
hasFooter && /* @__PURE__ */ jsx(
|
|
106
|
+
ContentFooter,
|
|
107
|
+
{
|
|
108
|
+
footerContent,
|
|
109
|
+
footerActions
|
|
110
|
+
}
|
|
111
|
+
)
|
|
112
|
+
] })
|
|
61
113
|
] }) });
|
|
62
114
|
};
|
|
63
115
|
var ContentBlock = ({ isCollapsible, ...props }) => {
|
|
@@ -72,8 +124,12 @@ var ContentBlock = ({ isCollapsible, ...props }) => {
|
|
|
72
124
|
children,
|
|
73
125
|
isLoading,
|
|
74
126
|
headerActions,
|
|
75
|
-
contentProps
|
|
127
|
+
contentProps,
|
|
128
|
+
flushLayout,
|
|
129
|
+
footerContent,
|
|
130
|
+
footerActions
|
|
76
131
|
} = props;
|
|
132
|
+
const hasFooter = !!(footerContent || footerActions);
|
|
77
133
|
return /* @__PURE__ */ jsxs(Container, { as: "section", children: [
|
|
78
134
|
/* @__PURE__ */ jsx(
|
|
79
135
|
ContentHeader,
|
|
@@ -85,7 +141,23 @@ var ContentBlock = ({ isCollapsible, ...props }) => {
|
|
|
85
141
|
headerActions
|
|
86
142
|
}
|
|
87
143
|
),
|
|
88
|
-
/* @__PURE__ */ jsx(
|
|
144
|
+
/* @__PURE__ */ jsx(
|
|
145
|
+
ContentArea,
|
|
146
|
+
{
|
|
147
|
+
isLoading,
|
|
148
|
+
contentProps,
|
|
149
|
+
flushLayout,
|
|
150
|
+
hasFooter,
|
|
151
|
+
children
|
|
152
|
+
}
|
|
153
|
+
),
|
|
154
|
+
hasFooter && /* @__PURE__ */ jsx(
|
|
155
|
+
ContentFooter,
|
|
156
|
+
{
|
|
157
|
+
footerContent,
|
|
158
|
+
footerActions
|
|
159
|
+
}
|
|
160
|
+
)
|
|
89
161
|
] });
|
|
90
162
|
};
|
|
91
163
|
var ContentBlock_default = ContentBlock;
|
package/dist/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/ContentBlock.tsx","../../src/index.ts"],"sourcesContent":["import React, { useState } from \"react\";\nimport { Box } from \"@sproutsocial/seeds-react-box\";\nimport { Loader } from \"@sproutsocial/seeds-react-loader\";\nimport { Collapsible } from \"@sproutsocial/seeds-react-collapsible\";\nimport {\n ContentHeader,\n CollapsibleContentHeader,\n} from \"@sproutsocial/seeds-react-content-header\";\nimport { Container } from \"@sproutsocial/seeds-react-container\";\n\ninterface ContentBlockBaseProps {\n title: string;\n subtitle?: string;\n titleAs?: \"h1\" | \"h2\" | \"h3\" | \"h4\";\n subtitleAs?: \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"p\";\n isLoading?: boolean;\n children: React.ReactNode;\n headerActions?: React.ReactNode;\n contentProps?: React.ComponentProps<typeof Box>;\n}\n\ntype StaticContentBlockProps = ContentBlockBaseProps & {\n isCollapsible?: false;\n isOpen?: never;\n defaultOpen?: never;\n onOpenChange?: never;\n};\n\ntype CollapsibleContentBlockProps = ContentBlockBaseProps & {\n /** Enables collapse/expand behavior on the content area */\n isCollapsible: true;\n /** Controlled open state. When provided, the component is in controlled mode. */\n isOpen?: boolean;\n /** Initial open state for uncontrolled mode. Defaults to true. */\n defaultOpen?: boolean;\n /** Called when the open state changes */\n onOpenChange?: (open: boolean) => void;\n};\n\nexport type ContentBlockProps =\n | StaticContentBlockProps\n | CollapsibleContentBlockProps;\n\nconst ContentArea = ({\n isLoading,\n children,\n contentProps,\n}: Pick
|
|
1
|
+
{"version":3,"sources":["../../src/ContentBlock.tsx","../../src/styles.ts","../../src/index.ts"],"sourcesContent":["import React, { useState } from \"react\";\nimport { Box } from \"@sproutsocial/seeds-react-box\";\nimport { Loader } from \"@sproutsocial/seeds-react-loader\";\nimport { Collapsible } from \"@sproutsocial/seeds-react-collapsible\";\nimport {\n ContentHeader,\n CollapsibleContentHeader,\n} from \"@sproutsocial/seeds-react-content-header\";\nimport { Container } from \"@sproutsocial/seeds-react-container\";\nimport { FooterContainer } from \"./styles\";\n\ninterface ContentBlockBaseProps {\n title: string;\n subtitle?: string;\n titleAs?: \"h1\" | \"h2\" | \"h3\" | \"h4\";\n subtitleAs?: \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"p\";\n isLoading?: boolean;\n children: React.ReactNode;\n headerActions?: React.ReactNode;\n contentProps?: React.ComponentProps<typeof Box>;\n /** Sets content area padding to 0 */\n flushLayout?: boolean;\n /** Left slot of the footer area */\n footerContent?: React.ReactNode;\n /** Right slot of the footer area */\n footerActions?: React.ReactNode;\n}\n\ntype StaticContentBlockProps = ContentBlockBaseProps & {\n isCollapsible?: false;\n isOpen?: never;\n defaultOpen?: never;\n onOpenChange?: never;\n};\n\ntype CollapsibleContentBlockProps = ContentBlockBaseProps & {\n /** Enables collapse/expand behavior on the content area */\n isCollapsible: true;\n /** Controlled open state. When provided, the component is in controlled mode. */\n isOpen?: boolean;\n /** Initial open state for uncontrolled mode. Defaults to true. */\n defaultOpen?: boolean;\n /** Called when the open state changes */\n onOpenChange?: (open: boolean) => void;\n};\n\nexport type ContentBlockProps =\n | StaticContentBlockProps\n | CollapsibleContentBlockProps;\n\nconst ContentArea = ({\n isLoading,\n children,\n contentProps,\n flushLayout,\n hasFooter,\n}: Pick<\n ContentBlockBaseProps,\n \"isLoading\" | \"children\" | \"contentProps\" | \"flushLayout\"\n> & { hasFooter?: boolean }) => (\n <Box\n p={flushLayout ? 0 : 400}\n {...contentProps}\n borderBottomLeftRadius={hasFooter ? undefined : \"outer\"}\n borderBottomRightRadius={hasFooter ? undefined : \"outer\"}\n >\n {isLoading ? (\n <Box my={400}>\n <Loader delay={false} />\n </Box>\n ) : (\n children\n )}\n </Box>\n);\n\nconst ContentFooter = ({\n footerContent,\n footerActions,\n}: {\n footerContent?: React.ReactNode;\n footerActions?: React.ReactNode;\n}) => (\n <FooterContainer>\n {footerContent && <span>{footerContent}</span>}\n {footerActions && <span>{footerActions}</span>}\n </FooterContainer>\n);\n\nconst CollapsibleContentBlock = ({\n title,\n subtitle,\n titleAs = \"h2\",\n subtitleAs = \"h3\",\n children,\n isLoading,\n headerActions,\n contentProps,\n flushLayout,\n footerContent,\n footerActions,\n isOpen: controlledIsOpen,\n defaultOpen = true,\n onOpenChange,\n}: Omit<CollapsibleContentBlockProps, \"isCollapsible\">) => {\n const [internalOpen, setInternalOpen] = useState(defaultOpen);\n const open = controlledIsOpen !== undefined ? controlledIsOpen : internalOpen;\n const hasFooter = !!(footerContent || footerActions);\n\n const handleOpenChange = (newOpen: boolean) => {\n if (controlledIsOpen === undefined) {\n setInternalOpen(newOpen);\n }\n onOpenChange?.(newOpen);\n };\n\n return (\n <Collapsible isOpen={open} onOpenChange={handleOpenChange}>\n <Container as=\"section\">\n <CollapsibleContentHeader\n title={title}\n subtitle={subtitle}\n headingLevel={titleAs}\n subtitleAs={subtitleAs}\n headerActions={headerActions}\n trigger={Collapsible.Trigger}\n triggerPosition=\"left\"\n />\n <Collapsible.Panel>\n <ContentArea\n isLoading={isLoading}\n contentProps={contentProps}\n flushLayout={flushLayout}\n hasFooter={hasFooter}\n >\n {children}\n </ContentArea>\n {hasFooter && (\n <ContentFooter\n footerContent={footerContent}\n footerActions={footerActions}\n />\n )}\n </Collapsible.Panel>\n </Container>\n </Collapsible>\n );\n};\n\nconst ContentBlock = ({ isCollapsible, ...props }: ContentBlockProps) => {\n if (isCollapsible) {\n return <CollapsibleContentBlock {...props} />;\n }\n\n const {\n title,\n subtitle,\n titleAs = \"h2\",\n subtitleAs = \"h3\",\n children,\n isLoading,\n headerActions,\n contentProps,\n flushLayout,\n footerContent,\n footerActions,\n } = props;\n\n const hasFooter = !!(footerContent || footerActions);\n\n return (\n <Container as=\"section\">\n <ContentHeader\n title={title}\n subtitle={subtitle}\n headingLevel={titleAs}\n subtitleAs={subtitleAs}\n headerActions={headerActions}\n />\n <ContentArea\n isLoading={isLoading}\n contentProps={contentProps}\n flushLayout={flushLayout}\n hasFooter={hasFooter}\n >\n {children}\n </ContentArea>\n {hasFooter && (\n <ContentFooter\n footerContent={footerContent}\n footerActions={footerActions}\n />\n )}\n </Container>\n );\n};\n\nexport default ContentBlock;\n","import styled from \"styled-components\";\nimport { Box } from \"@sproutsocial/seeds-react-box\";\n\nexport const ContentBlockContent = styled(Box)`\n padding: ${({ theme }) => theme.space[400]};\n`;\n\nexport const FooterContainer = styled.div`\n box-sizing: border-box;\n display: flex;\n justify-content: space-between;\n align-items: center;\n width: 100%;\n padding: ${({ theme }) => theme.space[400]};\n border-top: ${({ theme }) => theme.borderWidths[500]} solid\n ${({ theme }) => theme.colors.container.border.base};\n border-bottom-left-radius: ${({ theme }) => theme.radii.outer};\n border-bottom-right-radius: ${({ theme }) => theme.radii.outer};\n`;\n","import ContentBlock from \"./ContentBlock\";\n\nexport type { ContentBlockProps } from \"./ContentBlock\";\n\nexport default ContentBlock;\nexport { ContentBlock };\n"],"mappings":"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","names":["Box","Box"]}
|
package/dist/index.d.mts
CHANGED
|
@@ -11,6 +11,12 @@ interface ContentBlockBaseProps {
|
|
|
11
11
|
children: React.ReactNode;
|
|
12
12
|
headerActions?: React.ReactNode;
|
|
13
13
|
contentProps?: React.ComponentProps<typeof Box>;
|
|
14
|
+
/** Sets content area padding to 0 */
|
|
15
|
+
flushLayout?: boolean;
|
|
16
|
+
/** Left slot of the footer area */
|
|
17
|
+
footerContent?: React.ReactNode;
|
|
18
|
+
/** Right slot of the footer area */
|
|
19
|
+
footerActions?: React.ReactNode;
|
|
14
20
|
}
|
|
15
21
|
type StaticContentBlockProps = ContentBlockBaseProps & {
|
|
16
22
|
isCollapsible?: false;
|
package/dist/index.d.ts
CHANGED
|
@@ -11,6 +11,12 @@ interface ContentBlockBaseProps {
|
|
|
11
11
|
children: React.ReactNode;
|
|
12
12
|
headerActions?: React.ReactNode;
|
|
13
13
|
contentProps?: React.ComponentProps<typeof Box>;
|
|
14
|
+
/** Sets content area padding to 0 */
|
|
15
|
+
flushLayout?: boolean;
|
|
16
|
+
/** Left slot of the footer area */
|
|
17
|
+
footerContent?: React.ReactNode;
|
|
18
|
+
/** Right slot of the footer area */
|
|
19
|
+
footerActions?: React.ReactNode;
|
|
14
20
|
}
|
|
15
21
|
type StaticContentBlockProps = ContentBlockBaseProps & {
|
|
16
22
|
isCollapsible?: false;
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
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
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/index.ts
|
|
@@ -27,26 +37,56 @@ module.exports = __toCommonJS(index_exports);
|
|
|
27
37
|
|
|
28
38
|
// src/ContentBlock.tsx
|
|
29
39
|
var import_react = require("react");
|
|
30
|
-
var
|
|
40
|
+
var import_seeds_react_box2 = require("@sproutsocial/seeds-react-box");
|
|
31
41
|
var import_seeds_react_loader = require("@sproutsocial/seeds-react-loader");
|
|
32
42
|
var import_seeds_react_collapsible = require("@sproutsocial/seeds-react-collapsible");
|
|
33
43
|
var import_seeds_react_content_header = require("@sproutsocial/seeds-react-content-header");
|
|
34
44
|
var import_seeds_react_container = require("@sproutsocial/seeds-react-container");
|
|
45
|
+
|
|
46
|
+
// src/styles.ts
|
|
47
|
+
var import_styled_components = __toESM(require("styled-components"));
|
|
48
|
+
var import_seeds_react_box = require("@sproutsocial/seeds-react-box");
|
|
49
|
+
var ContentBlockContent = (0, import_styled_components.default)(import_seeds_react_box.Box)`
|
|
50
|
+
padding: ${({ theme }) => theme.space[400]};
|
|
51
|
+
`;
|
|
52
|
+
var FooterContainer = import_styled_components.default.div`
|
|
53
|
+
box-sizing: border-box;
|
|
54
|
+
display: flex;
|
|
55
|
+
justify-content: space-between;
|
|
56
|
+
align-items: center;
|
|
57
|
+
width: 100%;
|
|
58
|
+
padding: ${({ theme }) => theme.space[400]};
|
|
59
|
+
border-top: ${({ theme }) => theme.borderWidths[500]} solid
|
|
60
|
+
${({ theme }) => theme.colors.container.border.base};
|
|
61
|
+
border-bottom-left-radius: ${({ theme }) => theme.radii.outer};
|
|
62
|
+
border-bottom-right-radius: ${({ theme }) => theme.radii.outer};
|
|
63
|
+
`;
|
|
64
|
+
|
|
65
|
+
// src/ContentBlock.tsx
|
|
35
66
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
36
67
|
var ContentArea = ({
|
|
37
68
|
isLoading,
|
|
38
69
|
children,
|
|
39
|
-
contentProps
|
|
70
|
+
contentProps,
|
|
71
|
+
flushLayout,
|
|
72
|
+
hasFooter
|
|
40
73
|
}) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
41
|
-
|
|
74
|
+
import_seeds_react_box2.Box,
|
|
42
75
|
{
|
|
43
|
-
p: 400,
|
|
76
|
+
p: flushLayout ? 0 : 400,
|
|
44
77
|
...contentProps,
|
|
45
|
-
borderBottomLeftRadius: "outer",
|
|
46
|
-
borderBottomRightRadius: "outer",
|
|
47
|
-
children: isLoading ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
78
|
+
borderBottomLeftRadius: hasFooter ? void 0 : "outer",
|
|
79
|
+
borderBottomRightRadius: hasFooter ? void 0 : "outer",
|
|
80
|
+
children: isLoading ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_seeds_react_box2.Box, { my: 400, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_seeds_react_loader.Loader, { delay: false }) }) : children
|
|
48
81
|
}
|
|
49
82
|
);
|
|
83
|
+
var ContentFooter = ({
|
|
84
|
+
footerContent,
|
|
85
|
+
footerActions
|
|
86
|
+
}) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(FooterContainer, { children: [
|
|
87
|
+
footerContent && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: footerContent }),
|
|
88
|
+
footerActions && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: footerActions })
|
|
89
|
+
] });
|
|
50
90
|
var CollapsibleContentBlock = ({
|
|
51
91
|
title,
|
|
52
92
|
subtitle,
|
|
@@ -56,12 +96,16 @@ var CollapsibleContentBlock = ({
|
|
|
56
96
|
isLoading,
|
|
57
97
|
headerActions,
|
|
58
98
|
contentProps,
|
|
99
|
+
flushLayout,
|
|
100
|
+
footerContent,
|
|
101
|
+
footerActions,
|
|
59
102
|
isOpen: controlledIsOpen,
|
|
60
103
|
defaultOpen = true,
|
|
61
104
|
onOpenChange
|
|
62
105
|
}) => {
|
|
63
106
|
const [internalOpen, setInternalOpen] = (0, import_react.useState)(defaultOpen);
|
|
64
107
|
const open = controlledIsOpen !== void 0 ? controlledIsOpen : internalOpen;
|
|
108
|
+
const hasFooter = !!(footerContent || footerActions);
|
|
65
109
|
const handleOpenChange = (newOpen) => {
|
|
66
110
|
if (controlledIsOpen === void 0) {
|
|
67
111
|
setInternalOpen(newOpen);
|
|
@@ -81,7 +125,25 @@ var CollapsibleContentBlock = ({
|
|
|
81
125
|
triggerPosition: "left"
|
|
82
126
|
}
|
|
83
127
|
),
|
|
84
|
-
/* @__PURE__ */ (0, import_jsx_runtime.
|
|
128
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_seeds_react_collapsible.Collapsible.Panel, { children: [
|
|
129
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
130
|
+
ContentArea,
|
|
131
|
+
{
|
|
132
|
+
isLoading,
|
|
133
|
+
contentProps,
|
|
134
|
+
flushLayout,
|
|
135
|
+
hasFooter,
|
|
136
|
+
children
|
|
137
|
+
}
|
|
138
|
+
),
|
|
139
|
+
hasFooter && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
140
|
+
ContentFooter,
|
|
141
|
+
{
|
|
142
|
+
footerContent,
|
|
143
|
+
footerActions
|
|
144
|
+
}
|
|
145
|
+
)
|
|
146
|
+
] })
|
|
85
147
|
] }) });
|
|
86
148
|
};
|
|
87
149
|
var ContentBlock = ({ isCollapsible, ...props }) => {
|
|
@@ -96,8 +158,12 @@ var ContentBlock = ({ isCollapsible, ...props }) => {
|
|
|
96
158
|
children,
|
|
97
159
|
isLoading,
|
|
98
160
|
headerActions,
|
|
99
|
-
contentProps
|
|
161
|
+
contentProps,
|
|
162
|
+
flushLayout,
|
|
163
|
+
footerContent,
|
|
164
|
+
footerActions
|
|
100
165
|
} = props;
|
|
166
|
+
const hasFooter = !!(footerContent || footerActions);
|
|
101
167
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_seeds_react_container.Container, { as: "section", children: [
|
|
102
168
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
103
169
|
import_seeds_react_content_header.ContentHeader,
|
|
@@ -109,7 +175,23 @@ var ContentBlock = ({ isCollapsible, ...props }) => {
|
|
|
109
175
|
headerActions
|
|
110
176
|
}
|
|
111
177
|
),
|
|
112
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
178
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
179
|
+
ContentArea,
|
|
180
|
+
{
|
|
181
|
+
isLoading,
|
|
182
|
+
contentProps,
|
|
183
|
+
flushLayout,
|
|
184
|
+
hasFooter,
|
|
185
|
+
children
|
|
186
|
+
}
|
|
187
|
+
),
|
|
188
|
+
hasFooter && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
189
|
+
ContentFooter,
|
|
190
|
+
{
|
|
191
|
+
footerContent,
|
|
192
|
+
footerActions
|
|
193
|
+
}
|
|
194
|
+
)
|
|
113
195
|
] });
|
|
114
196
|
};
|
|
115
197
|
var ContentBlock_default = ContentBlock;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/ContentBlock.tsx"],"sourcesContent":["import ContentBlock from \"./ContentBlock\";\n\nexport type { ContentBlockProps } from \"./ContentBlock\";\n\nexport default ContentBlock;\nexport { ContentBlock };\n","import React, { useState } from \"react\";\nimport { Box } from \"@sproutsocial/seeds-react-box\";\nimport { Loader } from \"@sproutsocial/seeds-react-loader\";\nimport { Collapsible } from \"@sproutsocial/seeds-react-collapsible\";\nimport {\n ContentHeader,\n CollapsibleContentHeader,\n} from \"@sproutsocial/seeds-react-content-header\";\nimport { Container } from \"@sproutsocial/seeds-react-container\";\n\ninterface ContentBlockBaseProps {\n title: string;\n subtitle?: string;\n titleAs?: \"h1\" | \"h2\" | \"h3\" | \"h4\";\n subtitleAs?: \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"p\";\n isLoading?: boolean;\n children: React.ReactNode;\n headerActions?: React.ReactNode;\n contentProps?: React.ComponentProps<typeof Box>;\n}\n\ntype StaticContentBlockProps = ContentBlockBaseProps & {\n isCollapsible?: false;\n isOpen?: never;\n defaultOpen?: never;\n onOpenChange?: never;\n};\n\ntype CollapsibleContentBlockProps = ContentBlockBaseProps & {\n /** Enables collapse/expand behavior on the content area */\n isCollapsible: true;\n /** Controlled open state. When provided, the component is in controlled mode. */\n isOpen?: boolean;\n /** Initial open state for uncontrolled mode. Defaults to true. */\n defaultOpen?: boolean;\n /** Called when the open state changes */\n onOpenChange?: (open: boolean) => void;\n};\n\nexport type ContentBlockProps =\n | StaticContentBlockProps\n | CollapsibleContentBlockProps;\n\nconst ContentArea = ({\n isLoading,\n children,\n contentProps,\n}: Pick
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/ContentBlock.tsx","../src/styles.ts"],"sourcesContent":["import ContentBlock from \"./ContentBlock\";\n\nexport type { ContentBlockProps } from \"./ContentBlock\";\n\nexport default ContentBlock;\nexport { ContentBlock };\n","import React, { useState } from \"react\";\nimport { Box } from \"@sproutsocial/seeds-react-box\";\nimport { Loader } from \"@sproutsocial/seeds-react-loader\";\nimport { Collapsible } from \"@sproutsocial/seeds-react-collapsible\";\nimport {\n ContentHeader,\n CollapsibleContentHeader,\n} from \"@sproutsocial/seeds-react-content-header\";\nimport { Container } from \"@sproutsocial/seeds-react-container\";\nimport { FooterContainer } from \"./styles\";\n\ninterface ContentBlockBaseProps {\n title: string;\n subtitle?: string;\n titleAs?: \"h1\" | \"h2\" | \"h3\" | \"h4\";\n subtitleAs?: \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"p\";\n isLoading?: boolean;\n children: React.ReactNode;\n headerActions?: React.ReactNode;\n contentProps?: React.ComponentProps<typeof Box>;\n /** Sets content area padding to 0 */\n flushLayout?: boolean;\n /** Left slot of the footer area */\n footerContent?: React.ReactNode;\n /** Right slot of the footer area */\n footerActions?: React.ReactNode;\n}\n\ntype StaticContentBlockProps = ContentBlockBaseProps & {\n isCollapsible?: false;\n isOpen?: never;\n defaultOpen?: never;\n onOpenChange?: never;\n};\n\ntype CollapsibleContentBlockProps = ContentBlockBaseProps & {\n /** Enables collapse/expand behavior on the content area */\n isCollapsible: true;\n /** Controlled open state. When provided, the component is in controlled mode. */\n isOpen?: boolean;\n /** Initial open state for uncontrolled mode. Defaults to true. */\n defaultOpen?: boolean;\n /** Called when the open state changes */\n onOpenChange?: (open: boolean) => void;\n};\n\nexport type ContentBlockProps =\n | StaticContentBlockProps\n | CollapsibleContentBlockProps;\n\nconst ContentArea = ({\n isLoading,\n children,\n contentProps,\n flushLayout,\n hasFooter,\n}: Pick<\n ContentBlockBaseProps,\n \"isLoading\" | \"children\" | \"contentProps\" | \"flushLayout\"\n> & { hasFooter?: boolean }) => (\n <Box\n p={flushLayout ? 0 : 400}\n {...contentProps}\n borderBottomLeftRadius={hasFooter ? undefined : \"outer\"}\n borderBottomRightRadius={hasFooter ? undefined : \"outer\"}\n >\n {isLoading ? (\n <Box my={400}>\n <Loader delay={false} />\n </Box>\n ) : (\n children\n )}\n </Box>\n);\n\nconst ContentFooter = ({\n footerContent,\n footerActions,\n}: {\n footerContent?: React.ReactNode;\n footerActions?: React.ReactNode;\n}) => (\n <FooterContainer>\n {footerContent && <span>{footerContent}</span>}\n {footerActions && <span>{footerActions}</span>}\n </FooterContainer>\n);\n\nconst CollapsibleContentBlock = ({\n title,\n subtitle,\n titleAs = \"h2\",\n subtitleAs = \"h3\",\n children,\n isLoading,\n headerActions,\n contentProps,\n flushLayout,\n footerContent,\n footerActions,\n isOpen: controlledIsOpen,\n defaultOpen = true,\n onOpenChange,\n}: Omit<CollapsibleContentBlockProps, \"isCollapsible\">) => {\n const [internalOpen, setInternalOpen] = useState(defaultOpen);\n const open = controlledIsOpen !== undefined ? controlledIsOpen : internalOpen;\n const hasFooter = !!(footerContent || footerActions);\n\n const handleOpenChange = (newOpen: boolean) => {\n if (controlledIsOpen === undefined) {\n setInternalOpen(newOpen);\n }\n onOpenChange?.(newOpen);\n };\n\n return (\n <Collapsible isOpen={open} onOpenChange={handleOpenChange}>\n <Container as=\"section\">\n <CollapsibleContentHeader\n title={title}\n subtitle={subtitle}\n headingLevel={titleAs}\n subtitleAs={subtitleAs}\n headerActions={headerActions}\n trigger={Collapsible.Trigger}\n triggerPosition=\"left\"\n />\n <Collapsible.Panel>\n <ContentArea\n isLoading={isLoading}\n contentProps={contentProps}\n flushLayout={flushLayout}\n hasFooter={hasFooter}\n >\n {children}\n </ContentArea>\n {hasFooter && (\n <ContentFooter\n footerContent={footerContent}\n footerActions={footerActions}\n />\n )}\n </Collapsible.Panel>\n </Container>\n </Collapsible>\n );\n};\n\nconst ContentBlock = ({ isCollapsible, ...props }: ContentBlockProps) => {\n if (isCollapsible) {\n return <CollapsibleContentBlock {...props} />;\n }\n\n const {\n title,\n subtitle,\n titleAs = \"h2\",\n subtitleAs = \"h3\",\n children,\n isLoading,\n headerActions,\n contentProps,\n flushLayout,\n footerContent,\n footerActions,\n } = props;\n\n const hasFooter = !!(footerContent || footerActions);\n\n return (\n <Container as=\"section\">\n <ContentHeader\n title={title}\n subtitle={subtitle}\n headingLevel={titleAs}\n subtitleAs={subtitleAs}\n headerActions={headerActions}\n />\n <ContentArea\n isLoading={isLoading}\n contentProps={contentProps}\n flushLayout={flushLayout}\n hasFooter={hasFooter}\n >\n {children}\n </ContentArea>\n {hasFooter && (\n <ContentFooter\n footerContent={footerContent}\n footerActions={footerActions}\n />\n )}\n </Container>\n );\n};\n\nexport default ContentBlock;\n","import styled from \"styled-components\";\nimport { Box } from \"@sproutsocial/seeds-react-box\";\n\nexport const ContentBlockContent = styled(Box)`\n padding: ${({ theme }) => theme.space[400]};\n`;\n\nexport const FooterContainer = styled.div`\n box-sizing: border-box;\n display: flex;\n justify-content: space-between;\n align-items: center;\n width: 100%;\n padding: ${({ theme }) => theme.space[400]};\n border-top: ${({ theme }) => theme.borderWidths[500]} solid\n ${({ theme }) => theme.colors.container.border.base};\n border-bottom-left-radius: ${({ theme }) => theme.radii.outer};\n border-bottom-right-radius: ${({ theme }) => theme.radii.outer};\n`;\n"],"mappings":"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","names":["import_seeds_react_box","styled"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sproutsocial/seeds-react-content-block",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Seeds React ContentBlock",
|
|
5
5
|
"author": "Sprout Social, Inc.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -19,10 +19,10 @@
|
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"@sproutsocial/seeds-react-box": "^1.1.16",
|
|
22
|
-
"@sproutsocial/seeds-react-button": "^2.0.
|
|
22
|
+
"@sproutsocial/seeds-react-button": "^2.0.3",
|
|
23
23
|
"@sproutsocial/seeds-react-collapsible": "^2.0.0",
|
|
24
24
|
"@sproutsocial/seeds-react-container": "^0.3.10",
|
|
25
|
-
"@sproutsocial/seeds-react-content-header": "^0.2.
|
|
25
|
+
"@sproutsocial/seeds-react-content-header": "^0.2.9",
|
|
26
26
|
"@sproutsocial/seeds-react-loader": "^1.0.16",
|
|
27
27
|
"@sproutsocial/seeds-react-theme": "^3.6.2"
|
|
28
28
|
},
|
|
@@ -2,6 +2,7 @@ import React, { useState } from "react";
|
|
|
2
2
|
import ContentBlock from "./ContentBlock";
|
|
3
3
|
import type { Meta, StoryObj } from "@storybook/react";
|
|
4
4
|
import { Text } from "@sproutsocial/seeds-react-text";
|
|
5
|
+
import { Box } from "@sproutsocial/seeds-react-box";
|
|
5
6
|
import { Button } from "@sproutsocial/seeds-react-button";
|
|
6
7
|
import { Icon } from "@sproutsocial/seeds-react-icon";
|
|
7
8
|
|
|
@@ -56,6 +57,45 @@ export const WithContentStyles: Story = {
|
|
|
56
57
|
),
|
|
57
58
|
};
|
|
58
59
|
|
|
60
|
+
export const WithFooter: Story = {
|
|
61
|
+
render: (args) => (
|
|
62
|
+
<ContentBlock
|
|
63
|
+
{...args}
|
|
64
|
+
footerContent={
|
|
65
|
+
<Text.SmallBodyCopy>Footer description text</Text.SmallBodyCopy>
|
|
66
|
+
}
|
|
67
|
+
footerActions={
|
|
68
|
+
<Button
|
|
69
|
+
href="https://app.sproutsocial.com/dashboard/"
|
|
70
|
+
appearance="secondary"
|
|
71
|
+
>
|
|
72
|
+
See profile performance
|
|
73
|
+
</Button>
|
|
74
|
+
}
|
|
75
|
+
/>
|
|
76
|
+
),
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export const WithFlushContent: Story = {
|
|
80
|
+
render: (args) => (
|
|
81
|
+
<ContentBlock
|
|
82
|
+
{...args}
|
|
83
|
+
flushLayout
|
|
84
|
+
footerContent={
|
|
85
|
+
<Text.SmallBodyCopy>Footer description text</Text.SmallBodyCopy>
|
|
86
|
+
}
|
|
87
|
+
>
|
|
88
|
+
<Box p={400}>Impressions</Box>
|
|
89
|
+
<Box p={400} borderTop={500} borderColor="container.border.base">
|
|
90
|
+
Engagements
|
|
91
|
+
</Box>
|
|
92
|
+
<Box p={400} borderTop={500} borderColor="container.border.base">
|
|
93
|
+
Followers Gained
|
|
94
|
+
</Box>
|
|
95
|
+
</ContentBlock>
|
|
96
|
+
),
|
|
97
|
+
};
|
|
98
|
+
|
|
59
99
|
export const Collapsible: Story = {
|
|
60
100
|
render: (args) => (
|
|
61
101
|
<ContentBlock {...args} isCollapsible>
|
|
@@ -98,6 +138,31 @@ export const CollapsibleWithHeaderActions: Story = {
|
|
|
98
138
|
),
|
|
99
139
|
};
|
|
100
140
|
|
|
141
|
+
export const CollapsibleWithFooter: Story = {
|
|
142
|
+
name: "Collapsible with Footer",
|
|
143
|
+
render: (args) => (
|
|
144
|
+
<ContentBlock
|
|
145
|
+
{...args}
|
|
146
|
+
isCollapsible
|
|
147
|
+
footerContent={
|
|
148
|
+
<Text.SmallBodyCopy>Footer description text</Text.SmallBodyCopy>
|
|
149
|
+
}
|
|
150
|
+
footerActions={
|
|
151
|
+
<Button
|
|
152
|
+
href="https://app.sproutsocial.com/dashboard/"
|
|
153
|
+
appearance="secondary"
|
|
154
|
+
>
|
|
155
|
+
See profile performance
|
|
156
|
+
</Button>
|
|
157
|
+
}
|
|
158
|
+
>
|
|
159
|
+
<Text.SmallBodyCopy>
|
|
160
|
+
The footer collapses along with the content.
|
|
161
|
+
</Text.SmallBodyCopy>
|
|
162
|
+
</ContentBlock>
|
|
163
|
+
),
|
|
164
|
+
};
|
|
165
|
+
|
|
101
166
|
export const CollapsibleControlled: Story = {
|
|
102
167
|
name: "Collapsible (Controlled)",
|
|
103
168
|
render: (args) => {
|
package/src/ContentBlock.tsx
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
CollapsibleContentHeader,
|
|
8
8
|
} from "@sproutsocial/seeds-react-content-header";
|
|
9
9
|
import { Container } from "@sproutsocial/seeds-react-container";
|
|
10
|
+
import { FooterContainer } from "./styles";
|
|
10
11
|
|
|
11
12
|
interface ContentBlockBaseProps {
|
|
12
13
|
title: string;
|
|
@@ -17,6 +18,12 @@ interface ContentBlockBaseProps {
|
|
|
17
18
|
children: React.ReactNode;
|
|
18
19
|
headerActions?: React.ReactNode;
|
|
19
20
|
contentProps?: React.ComponentProps<typeof Box>;
|
|
21
|
+
/** Sets content area padding to 0 */
|
|
22
|
+
flushLayout?: boolean;
|
|
23
|
+
/** Left slot of the footer area */
|
|
24
|
+
footerContent?: React.ReactNode;
|
|
25
|
+
/** Right slot of the footer area */
|
|
26
|
+
footerActions?: React.ReactNode;
|
|
20
27
|
}
|
|
21
28
|
|
|
22
29
|
type StaticContentBlockProps = ContentBlockBaseProps & {
|
|
@@ -45,12 +52,17 @@ const ContentArea = ({
|
|
|
45
52
|
isLoading,
|
|
46
53
|
children,
|
|
47
54
|
contentProps,
|
|
48
|
-
|
|
55
|
+
flushLayout,
|
|
56
|
+
hasFooter,
|
|
57
|
+
}: Pick<
|
|
58
|
+
ContentBlockBaseProps,
|
|
59
|
+
"isLoading" | "children" | "contentProps" | "flushLayout"
|
|
60
|
+
> & { hasFooter?: boolean }) => (
|
|
49
61
|
<Box
|
|
50
|
-
p={400}
|
|
62
|
+
p={flushLayout ? 0 : 400}
|
|
51
63
|
{...contentProps}
|
|
52
|
-
borderBottomLeftRadius="outer"
|
|
53
|
-
borderBottomRightRadius="outer"
|
|
64
|
+
borderBottomLeftRadius={hasFooter ? undefined : "outer"}
|
|
65
|
+
borderBottomRightRadius={hasFooter ? undefined : "outer"}
|
|
54
66
|
>
|
|
55
67
|
{isLoading ? (
|
|
56
68
|
<Box my={400}>
|
|
@@ -62,6 +74,19 @@ const ContentArea = ({
|
|
|
62
74
|
</Box>
|
|
63
75
|
);
|
|
64
76
|
|
|
77
|
+
const ContentFooter = ({
|
|
78
|
+
footerContent,
|
|
79
|
+
footerActions,
|
|
80
|
+
}: {
|
|
81
|
+
footerContent?: React.ReactNode;
|
|
82
|
+
footerActions?: React.ReactNode;
|
|
83
|
+
}) => (
|
|
84
|
+
<FooterContainer>
|
|
85
|
+
{footerContent && <span>{footerContent}</span>}
|
|
86
|
+
{footerActions && <span>{footerActions}</span>}
|
|
87
|
+
</FooterContainer>
|
|
88
|
+
);
|
|
89
|
+
|
|
65
90
|
const CollapsibleContentBlock = ({
|
|
66
91
|
title,
|
|
67
92
|
subtitle,
|
|
@@ -71,12 +96,16 @@ const CollapsibleContentBlock = ({
|
|
|
71
96
|
isLoading,
|
|
72
97
|
headerActions,
|
|
73
98
|
contentProps,
|
|
99
|
+
flushLayout,
|
|
100
|
+
footerContent,
|
|
101
|
+
footerActions,
|
|
74
102
|
isOpen: controlledIsOpen,
|
|
75
103
|
defaultOpen = true,
|
|
76
104
|
onOpenChange,
|
|
77
105
|
}: Omit<CollapsibleContentBlockProps, "isCollapsible">) => {
|
|
78
106
|
const [internalOpen, setInternalOpen] = useState(defaultOpen);
|
|
79
107
|
const open = controlledIsOpen !== undefined ? controlledIsOpen : internalOpen;
|
|
108
|
+
const hasFooter = !!(footerContent || footerActions);
|
|
80
109
|
|
|
81
110
|
const handleOpenChange = (newOpen: boolean) => {
|
|
82
111
|
if (controlledIsOpen === undefined) {
|
|
@@ -98,9 +127,20 @@ const CollapsibleContentBlock = ({
|
|
|
98
127
|
triggerPosition="left"
|
|
99
128
|
/>
|
|
100
129
|
<Collapsible.Panel>
|
|
101
|
-
<ContentArea
|
|
130
|
+
<ContentArea
|
|
131
|
+
isLoading={isLoading}
|
|
132
|
+
contentProps={contentProps}
|
|
133
|
+
flushLayout={flushLayout}
|
|
134
|
+
hasFooter={hasFooter}
|
|
135
|
+
>
|
|
102
136
|
{children}
|
|
103
137
|
</ContentArea>
|
|
138
|
+
{hasFooter && (
|
|
139
|
+
<ContentFooter
|
|
140
|
+
footerContent={footerContent}
|
|
141
|
+
footerActions={footerActions}
|
|
142
|
+
/>
|
|
143
|
+
)}
|
|
104
144
|
</Collapsible.Panel>
|
|
105
145
|
</Container>
|
|
106
146
|
</Collapsible>
|
|
@@ -121,8 +161,13 @@ const ContentBlock = ({ isCollapsible, ...props }: ContentBlockProps) => {
|
|
|
121
161
|
isLoading,
|
|
122
162
|
headerActions,
|
|
123
163
|
contentProps,
|
|
164
|
+
flushLayout,
|
|
165
|
+
footerContent,
|
|
166
|
+
footerActions,
|
|
124
167
|
} = props;
|
|
125
168
|
|
|
169
|
+
const hasFooter = !!(footerContent || footerActions);
|
|
170
|
+
|
|
126
171
|
return (
|
|
127
172
|
<Container as="section">
|
|
128
173
|
<ContentHeader
|
|
@@ -132,9 +177,20 @@ const ContentBlock = ({ isCollapsible, ...props }: ContentBlockProps) => {
|
|
|
132
177
|
subtitleAs={subtitleAs}
|
|
133
178
|
headerActions={headerActions}
|
|
134
179
|
/>
|
|
135
|
-
<ContentArea
|
|
180
|
+
<ContentArea
|
|
181
|
+
isLoading={isLoading}
|
|
182
|
+
contentProps={contentProps}
|
|
183
|
+
flushLayout={flushLayout}
|
|
184
|
+
hasFooter={hasFooter}
|
|
185
|
+
>
|
|
136
186
|
{children}
|
|
137
187
|
</ContentArea>
|
|
188
|
+
{hasFooter && (
|
|
189
|
+
<ContentFooter
|
|
190
|
+
footerContent={footerContent}
|
|
191
|
+
footerActions={footerActions}
|
|
192
|
+
/>
|
|
193
|
+
)}
|
|
138
194
|
</Container>
|
|
139
195
|
);
|
|
140
196
|
};
|
|
@@ -73,6 +73,65 @@ describe("ContentBlock Features", () => {
|
|
|
73
73
|
});
|
|
74
74
|
});
|
|
75
75
|
|
|
76
|
+
describe("ContentBlock Footer", () => {
|
|
77
|
+
test("renders footerContent when provided", () => {
|
|
78
|
+
render(
|
|
79
|
+
<ContentBlock title="Title" footerContent={<span>Footer text</span>}>
|
|
80
|
+
Content
|
|
81
|
+
</ContentBlock>
|
|
82
|
+
);
|
|
83
|
+
expect(screen.getByText("Footer text")).toBeInTheDocument();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test("renders footerActions when provided", () => {
|
|
87
|
+
render(
|
|
88
|
+
<ContentBlock
|
|
89
|
+
title="Title"
|
|
90
|
+
footerActions={<button>Footer action</button>}
|
|
91
|
+
>
|
|
92
|
+
Content
|
|
93
|
+
</ContentBlock>
|
|
94
|
+
);
|
|
95
|
+
expect(screen.getByText("Footer action")).toBeInTheDocument();
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test("renders both footerContent and footerActions together", () => {
|
|
99
|
+
render(
|
|
100
|
+
<ContentBlock
|
|
101
|
+
title="Title"
|
|
102
|
+
footerContent={<span>Footer text</span>}
|
|
103
|
+
footerActions={<button>Footer action</button>}
|
|
104
|
+
>
|
|
105
|
+
Content
|
|
106
|
+
</ContentBlock>
|
|
107
|
+
);
|
|
108
|
+
expect(screen.getByText("Footer text")).toBeInTheDocument();
|
|
109
|
+
expect(screen.getByText("Footer action")).toBeInTheDocument();
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test("does not render footer when neither prop is provided", () => {
|
|
113
|
+
render(<ContentBlock title="Title">Content</ContentBlock>);
|
|
114
|
+
expect(screen.queryByText("Footer text")).not.toBeInTheDocument();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test("collapsible footer is visible when open and hidden when collapsed", () => {
|
|
118
|
+
render(
|
|
119
|
+
<ContentBlock
|
|
120
|
+
title="Title"
|
|
121
|
+
isCollapsible
|
|
122
|
+
footerContent={<span>Footer text</span>}
|
|
123
|
+
>
|
|
124
|
+
Content
|
|
125
|
+
</ContentBlock>
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
expect(screen.getByText("Footer text")).toBeInTheDocument();
|
|
129
|
+
|
|
130
|
+
fireEvent.click(screen.getByRole("button"));
|
|
131
|
+
expect(screen.queryByText("Footer text")).not.toBeInTheDocument();
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
76
135
|
describe("ContentBlock Collapsible", () => {
|
|
77
136
|
test("does not render chevron when not collapsible", () => {
|
|
78
137
|
render(
|
package/src/styles.ts
CHANGED
|
@@ -4,3 +4,16 @@ import { Box } from "@sproutsocial/seeds-react-box";
|
|
|
4
4
|
export const ContentBlockContent = styled(Box)`
|
|
5
5
|
padding: ${({ theme }) => theme.space[400]};
|
|
6
6
|
`;
|
|
7
|
+
|
|
8
|
+
export const FooterContainer = styled.div`
|
|
9
|
+
box-sizing: border-box;
|
|
10
|
+
display: flex;
|
|
11
|
+
justify-content: space-between;
|
|
12
|
+
align-items: center;
|
|
13
|
+
width: 100%;
|
|
14
|
+
padding: ${({ theme }) => theme.space[400]};
|
|
15
|
+
border-top: ${({ theme }) => theme.borderWidths[500]} solid
|
|
16
|
+
${({ theme }) => theme.colors.container.border.base};
|
|
17
|
+
border-bottom-left-radius: ${({ theme }) => theme.radii.outer};
|
|
18
|
+
border-bottom-right-radius: ${({ theme }) => theme.radii.outer};
|
|
19
|
+
`;
|