@hubspot/cms-component-library 0.3.5 → 0.3.6
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/components/componentLibrary/Accordion/llm.txt +1 -1
- package/components/componentLibrary/Card/llm.txt +1 -1
- package/components/componentLibrary/Form/ContentFields.tsx +1 -1
- package/components/componentLibrary/Form/index.tsx +12 -2
- package/components/componentLibrary/Form/islands/FormIsland.tsx +5 -5
- package/components/componentLibrary/Form/islands/LegacyFormIsland.tsx +77 -0
- package/components/componentLibrary/Form/types.ts +12 -4
- package/components/componentLibrary/Grid/llm.txt +13 -16
- package/components/componentLibrary/_patterns/checklist-and-examples.md +1 -1
- package/package.json +2 -2
|
@@ -439,7 +439,7 @@ The Accordion component follows accessibility best practices:
|
|
|
439
439
|
- **Gap selection**: Use any valid CSS length value (e.g., '8px', '16px', '24px', '48px') for spacing between items
|
|
440
440
|
- **Dynamic rendering**: Always provide unique `key` props when mapping arrays to AccordionItems
|
|
441
441
|
- **Rich content**: AccordionContent supports any HTML content including lists, paragraphs, images, and nested components
|
|
442
|
-
- **Prefer library components**: When adding content inside AccordionContent, prefer using library components (e.g., `List`, `
|
|
442
|
+
- **Prefer library components**: When adding content inside AccordionContent, prefer using library components (e.g., `List`, `Text` with `textFeatureSet="heading"` for headings) over raw HTML elements for consistent theming and styling
|
|
443
443
|
- **CSS Variables**: Override design tokens using CSS variables rather than hardcoding values
|
|
444
444
|
- **Single responsibility**: Keep each accordion item focused on one topic for better UX
|
|
445
445
|
|
|
@@ -182,4 +182,4 @@ The Card component follows accessibility best practices:
|
|
|
182
182
|
|
|
183
183
|
- **Flex**: Use for arranging cards in flexible layouts
|
|
184
184
|
- **Grid**: Use for creating responsive card grids
|
|
185
|
-
- **
|
|
185
|
+
- **Text**: Use with `textFeatureSet="heading"` for editable card titles
|
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
// @ts-expect-error -- ?island not typed
|
|
2
2
|
import FormIsland from './islands/FormIsland.js?island';
|
|
3
|
+
// @ts-expect-error -- ?island not typed
|
|
4
|
+
import LegacyFormIsland from './islands/LegacyFormIsland.js?island';
|
|
3
5
|
import { FormProps } from './types.js';
|
|
4
6
|
import { Island } from '@hubspot/cms-components';
|
|
5
7
|
import ContentFields from './ContentFields.js';
|
|
6
8
|
|
|
7
|
-
const FormComponent = (
|
|
8
|
-
|
|
9
|
+
const FormComponent = ({
|
|
10
|
+
formField,
|
|
11
|
+
formId,
|
|
12
|
+
formVersion,
|
|
13
|
+
...rest
|
|
14
|
+
}: FormProps) => {
|
|
15
|
+
const resolvedFormId = formField != null ? formField.form_id : formId;
|
|
16
|
+
const FormModule = formVersion === 'v4' ? FormIsland : LegacyFormIsland;
|
|
17
|
+
|
|
18
|
+
return <Island module={FormModule} formId={resolvedFormId} {...rest} />;
|
|
9
19
|
};
|
|
10
20
|
|
|
11
21
|
type FormComponentType = typeof FormComponent & {
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { useEffect } from 'react';
|
|
2
|
-
import { FormProps
|
|
3
|
-
import { getHubID } from '@hubspot/cms-components';
|
|
2
|
+
import { FormProps } from '../types.js';
|
|
3
|
+
import { getHubID, getHSEnv } from '@hubspot/cms-components';
|
|
4
4
|
|
|
5
5
|
const getScriptSrc = (portalId: number, env: string) => {
|
|
6
6
|
const host = env === 'qa' ? 'js.hsformsqa.net' : 'js.hsforms.net';
|
|
7
|
-
return `https://${host}/forms/embed/${portalId}.js`;
|
|
7
|
+
return `https://${host}/forms/embed/developer/${portalId}.js`;
|
|
8
8
|
};
|
|
9
9
|
|
|
10
|
-
const FormIsland = ({ formId
|
|
10
|
+
const FormIsland = ({ formId }: FormProps) => {
|
|
11
11
|
const portalId = getHubID();
|
|
12
|
-
const resolvedEnv =
|
|
12
|
+
const resolvedEnv = getHSEnv();
|
|
13
13
|
|
|
14
14
|
useEffect(() => {
|
|
15
15
|
if (!formId || !portalId) {
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { useEffect, useId } from 'react';
|
|
2
|
+
import { FormProps } from '../types.js';
|
|
3
|
+
import { getHubID, getHSEnv } from '@hubspot/cms-components';
|
|
4
|
+
|
|
5
|
+
declare global {
|
|
6
|
+
interface Window {
|
|
7
|
+
hbspt?: {
|
|
8
|
+
forms: {
|
|
9
|
+
create: (options: {
|
|
10
|
+
portalId: number;
|
|
11
|
+
formId: string;
|
|
12
|
+
env: 'qa' | 'prod';
|
|
13
|
+
target: string;
|
|
14
|
+
}) => void;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const getScriptSrc = (env: string) => {
|
|
21
|
+
const host = env === 'qa' ? 'js.hsformsqa.net' : 'js.hsforms.net';
|
|
22
|
+
return `//${host}/forms/embed/v2.js`;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const LegacyFormIsland = ({ formId }: FormProps) => {
|
|
26
|
+
const portalId = getHubID();
|
|
27
|
+
const resolvedEnv = getHSEnv();
|
|
28
|
+
const rawId = useId();
|
|
29
|
+
const containerId = `hs-legacy-form-${rawId.replace(/:/g, '')}`;
|
|
30
|
+
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
if (!formId || !portalId) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const scriptSrc = getScriptSrc(resolvedEnv);
|
|
37
|
+
|
|
38
|
+
const createForm = () => {
|
|
39
|
+
window.hbspt?.forms.create({
|
|
40
|
+
portalId,
|
|
41
|
+
formId,
|
|
42
|
+
env: resolvedEnv,
|
|
43
|
+
target: `#${containerId}`,
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const existingScript = document.querySelector(`script[src="${scriptSrc}"]`);
|
|
48
|
+
|
|
49
|
+
if (existingScript) {
|
|
50
|
+
if (window.hbspt) {
|
|
51
|
+
createForm();
|
|
52
|
+
} else {
|
|
53
|
+
existingScript.addEventListener('load', createForm);
|
|
54
|
+
return () => existingScript.removeEventListener('load', createForm);
|
|
55
|
+
}
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const script = document.createElement('script');
|
|
60
|
+
script.src = scriptSrc;
|
|
61
|
+
script.addEventListener('load', createForm);
|
|
62
|
+
document.head.appendChild(script);
|
|
63
|
+
|
|
64
|
+
return () => {
|
|
65
|
+
script.removeEventListener('load', createForm);
|
|
66
|
+
script.remove();
|
|
67
|
+
};
|
|
68
|
+
}, [formId, portalId, resolvedEnv, containerId]);
|
|
69
|
+
|
|
70
|
+
if (!formId || !portalId) {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return <div id={containerId} />;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export default LegacyFormIsland;
|
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
import { FormFieldDefaults } from '@hubspot/cms-components/fields';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
export type
|
|
2
|
+
|
|
3
|
+
export type FormProps = FormPropsWithField | FormPropsWithoutField;
|
|
4
|
+
|
|
5
|
+
export type FormPropsWithoutField = {
|
|
6
|
+
formField?: never;
|
|
6
7
|
formId: string;
|
|
8
|
+
formVersion: 'v4' | 'v3' | 'v2' | '';
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export type FormPropsWithField = {
|
|
12
|
+
formField: typeof FormFieldDefaults;
|
|
13
|
+
formId?: never;
|
|
14
|
+
formVersion?: never;
|
|
7
15
|
};
|
|
8
16
|
|
|
9
17
|
export type ContentFieldsProps = {
|
|
@@ -109,13 +109,13 @@ Grid/
|
|
|
109
109
|
|
|
110
110
|
**Purpose:** Wraps grid children to control their precise placement, spanning, and alignment within the Grid container. Can render as any HTML element or custom React component while maintaining grid positioning control.
|
|
111
111
|
|
|
112
|
-
**Key Feature:** The `as` prop is polymorphic - it accepts both HTML element strings ('div', 'section', etc.) and React component references (Button,
|
|
112
|
+
**Key Feature:** The `as` prop is polymorphic - it accepts both HTML element strings ('div', 'section', etc.) and React component references (Button, Text, custom components). GridItem handles grid placement while passing through all other props to the underlying component.
|
|
113
113
|
|
|
114
114
|
**Props:**
|
|
115
115
|
```tsx
|
|
116
116
|
{
|
|
117
117
|
as?: React.ElementType; // Any HTML element or React component (default: 'div')
|
|
118
|
-
// Examples: 'div', 'article', Button,
|
|
118
|
+
// Examples: 'div', 'article', Button, Text, CustomComponent
|
|
119
119
|
gridColumn?: string; // Grid column placement (e.g., '1 / 3', 'span 2', '2')
|
|
120
120
|
gridColumnMd?: string; // Grid column placement at tablet breakpoint (768px+)
|
|
121
121
|
gridColumnLg?: string; // Grid column placement at desktop breakpoint (1024px+)
|
|
@@ -148,7 +148,7 @@ GridItem's `as` prop provides flexibility in rendering while maintaining grid co
|
|
|
148
148
|
2. **React Components**: Pass component references directly
|
|
149
149
|
```tsx
|
|
150
150
|
<GridItem as={Button} buttonType="primary">Click me</GridItem>
|
|
151
|
-
<GridItem as={
|
|
151
|
+
<GridItem as={Text} fieldPath="title" />
|
|
152
152
|
```
|
|
153
153
|
|
|
154
154
|
3. **Prop Pass-Through**: All props beyond GridItem's own props are forwarded to the underlying component
|
|
@@ -158,10 +158,8 @@ GridItem's `as` prop provides flexibility in rendering while maintaining grid co
|
|
|
158
158
|
Link Button
|
|
159
159
|
</GridItem>
|
|
160
160
|
|
|
161
|
-
{/*
|
|
162
|
-
<GridItem as={
|
|
163
|
-
Styled Heading
|
|
164
|
-
</GridItem>
|
|
161
|
+
{/* Text-specific props are passed through */}
|
|
162
|
+
<GridItem as={Text} fieldPath="sectionTitle" className="custom-text" />
|
|
165
163
|
```
|
|
166
164
|
|
|
167
165
|
4. **Grid Positioning**: GridItem handles all grid-specific positioning regardless of the underlying component
|
|
@@ -199,7 +197,7 @@ import Grid, { GridItem } from '@hubspot/cms-component-library/Grid';
|
|
|
199
197
|
```tsx
|
|
200
198
|
import Grid, { GridItem } from '@hubspot/cms-component-library/Grid';
|
|
201
199
|
import Button from '@hubspot/cms-component-library/Button';
|
|
202
|
-
import
|
|
200
|
+
import Text from '@hubspot/cms-component-library/Text';
|
|
203
201
|
|
|
204
202
|
<Grid templateColumns="repeat(2, 1fr)" gap="16px">
|
|
205
203
|
{/* GridItem rendering as Button */}
|
|
@@ -211,13 +209,9 @@ import Heading from '@hubspot/cms-component-library/Heading';
|
|
|
211
209
|
Click Me
|
|
212
210
|
</GridItem>
|
|
213
211
|
|
|
214
|
-
{/* GridItem rendering as
|
|
215
|
-
<GridItem
|
|
216
|
-
|
|
217
|
-
headingLevel="h2"
|
|
218
|
-
displayAs="h3"
|
|
219
|
-
>
|
|
220
|
-
Section Title
|
|
212
|
+
{/* GridItem rendering as Text (heading feature set) */}
|
|
213
|
+
<GridItem>
|
|
214
|
+
<Text fieldPath="sectionTitle" />
|
|
221
215
|
</GridItem>
|
|
222
216
|
|
|
223
217
|
{/* Regular content */}
|
|
@@ -255,6 +249,9 @@ import Heading from '@hubspot/cms-component-library/Heading';
|
|
|
255
249
|
### Complex Layout Using Props (NO custom CSS)
|
|
256
250
|
|
|
257
251
|
```tsx
|
|
252
|
+
import Grid, { GridItem } from '@hubspot/cms-component-library/Grid';
|
|
253
|
+
import Text from '@hubspot/cms-component-library/Text';
|
|
254
|
+
|
|
258
255
|
{/* Dashboard layout - all positioning via props */}
|
|
259
256
|
<Grid
|
|
260
257
|
templateColumns="1fr"
|
|
@@ -267,7 +264,7 @@ import Heading from '@hubspot/cms-component-library/Heading';
|
|
|
267
264
|
gridColumn="1"
|
|
268
265
|
gridColumnMd="1 / -1"
|
|
269
266
|
>
|
|
270
|
-
<
|
|
267
|
+
<Text fieldPath="dashboardTitle" />
|
|
271
268
|
</GridItem>
|
|
272
269
|
|
|
273
270
|
{/* Sidebar - hidden on mobile, shown on tablet+ */}
|
|
@@ -83,7 +83,7 @@ When creating a new component in componentLibrary, ensure:
|
|
|
83
83
|
- [ ] Semantic HTML elements used appropriately
|
|
84
84
|
- [ ] ARIA roles added when needed (e.g., `role="separator"`)
|
|
85
85
|
- [ ] Icon purpose properly set (SEMANTIC vs DECORATIVE)
|
|
86
|
-
- [ ]
|
|
86
|
+
- [ ] Use `Text` with `textFeatureSet="heading"` for editable heading fields rather than raw HTML heading elements
|
|
87
87
|
|
|
88
88
|
## Reference Examples
|
|
89
89
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hubspot/cms-component-library",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.6",
|
|
4
4
|
"description": "HubSpot CMS React component library for building CMS modules",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"exports": {
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
},
|
|
22
22
|
"type": "module",
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@hubspot/cms-components": "1.2.
|
|
24
|
+
"@hubspot/cms-components": "1.2.19",
|
|
25
25
|
"sass-embedded": "^1.97.3"
|
|
26
26
|
},
|
|
27
27
|
"peerDependencies": {
|