@hubspot/cms-component-library 0.3.4 → 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/AccordionTitle/ContentFields.tsx +2 -20
- package/components/componentLibrary/Accordion/AccordionTitle/StyleFields.tsx +26 -0
- package/components/componentLibrary/Accordion/AccordionTitle/index.tsx +3 -0
- package/components/componentLibrary/Accordion/AccordionTitle/types.ts +3 -0
- package/components/componentLibrary/Accordion/llm.txt +20 -7
- package/components/componentLibrary/Card/llm.txt +1 -1
- package/components/componentLibrary/Form/ContentFields.tsx +19 -0
- package/components/componentLibrary/Form/index.tsx +28 -0
- package/components/componentLibrary/Form/islands/FormIsland.tsx +54 -0
- package/components/componentLibrary/Form/islands/LegacyFormIsland.tsx +77 -0
- package/components/componentLibrary/Form/islands/index.module.scss +3 -0
- package/components/componentLibrary/Form/types.ts +21 -0
- package/components/componentLibrary/Grid/llm.txt +13 -16
- package/components/componentLibrary/_patterns/checklist-and-examples.md +1 -1
- package/package.json +2 -2
|
@@ -1,31 +1,13 @@
|
|
|
1
|
-
import { TextField
|
|
1
|
+
import { TextField } from '@hubspot/cms-components/fields';
|
|
2
2
|
import type { ContentFieldsProps } from './types.js';
|
|
3
3
|
|
|
4
4
|
const ContentFields = ({
|
|
5
5
|
titleLabel = 'Title',
|
|
6
6
|
titleName = 'title',
|
|
7
7
|
titleDefault = 'Accordion Title',
|
|
8
|
-
iconLabel = 'Icon',
|
|
9
|
-
iconName = 'icon',
|
|
10
|
-
iconDefault = 'chevron',
|
|
11
8
|
}: ContentFieldsProps) => {
|
|
12
9
|
return (
|
|
13
|
-
|
|
14
|
-
<ChoiceField
|
|
15
|
-
label={iconLabel}
|
|
16
|
-
name={iconName}
|
|
17
|
-
display="buttons"
|
|
18
|
-
choices={[
|
|
19
|
-
['chevron', 'Chevron'],
|
|
20
|
-
['plus', 'Plus'],
|
|
21
|
-
['caret', 'Caret'],
|
|
22
|
-
]}
|
|
23
|
-
preset="expand_icon"
|
|
24
|
-
required={false}
|
|
25
|
-
default={iconDefault}
|
|
26
|
-
/>
|
|
27
|
-
<TextField label={titleLabel} name={titleName} default={titleDefault} />
|
|
28
|
-
</>
|
|
10
|
+
<TextField label={titleLabel} name={titleName} default={titleDefault} />
|
|
29
11
|
);
|
|
30
12
|
};
|
|
31
13
|
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ChoiceField } from '@hubspot/cms-components/fields';
|
|
2
|
+
import type { StyleFieldsProps } from './types.js';
|
|
3
|
+
|
|
4
|
+
const StyleFields = ({
|
|
5
|
+
iconLabel = 'Icon',
|
|
6
|
+
iconName = 'icon',
|
|
7
|
+
iconDefault = 'chevron',
|
|
8
|
+
}: StyleFieldsProps) => {
|
|
9
|
+
return (
|
|
10
|
+
<ChoiceField
|
|
11
|
+
label={iconLabel}
|
|
12
|
+
name={iconName}
|
|
13
|
+
display="buttons"
|
|
14
|
+
choices={[
|
|
15
|
+
['caret', 'Caret'],
|
|
16
|
+
['chevron', 'Chevron'],
|
|
17
|
+
['plus', 'Plus'],
|
|
18
|
+
]}
|
|
19
|
+
preset="expand_icon"
|
|
20
|
+
required={false}
|
|
21
|
+
default={iconDefault}
|
|
22
|
+
/>
|
|
23
|
+
);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export default StyleFields;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import styles from './index.module.scss';
|
|
2
2
|
import ContentFields from './ContentFields.js';
|
|
3
|
+
import StyleFields from './StyleFields.js';
|
|
3
4
|
import cx from '../../utils/classname.js';
|
|
4
5
|
import { AccordionTitleProps } from './types.js';
|
|
5
6
|
import { ChevronIcon, PlusIcon, MinusIcon, CaretIcon } from './icons.js';
|
|
@@ -39,9 +40,11 @@ const AccordionTitleComponent = ({
|
|
|
39
40
|
|
|
40
41
|
type AccordionTitleComponentType = typeof AccordionTitleComponent & {
|
|
41
42
|
ContentFields: typeof ContentFields;
|
|
43
|
+
StyleFields: typeof StyleFields;
|
|
42
44
|
};
|
|
43
45
|
|
|
44
46
|
const AccordionTitle = AccordionTitleComponent as AccordionTitleComponentType;
|
|
45
47
|
AccordionTitle.ContentFields = ContentFields;
|
|
48
|
+
AccordionTitle.StyleFields = StyleFields;
|
|
46
49
|
|
|
47
50
|
export default AccordionTitle;
|
|
@@ -52,7 +52,8 @@ Accordion/
|
|
|
52
52
|
├── AccordionTitle/
|
|
53
53
|
│ ├── index.tsx # AccordionTitle component
|
|
54
54
|
│ ├── types.ts # AccordionTitle TypeScript types
|
|
55
|
-
│ ├── ContentFields.tsx # HubSpot field definitions for title
|
|
55
|
+
│ ├── ContentFields.tsx # HubSpot field definitions for title text
|
|
56
|
+
│ ├── StyleFields.tsx # HubSpot field definitions for icon
|
|
56
57
|
│ ├── icons.tsx # SVG icon components (Chevron, Plus, Minus)
|
|
57
58
|
│ └── index.module.scss # CSS module for title styling
|
|
58
59
|
├── AccordionContent/
|
|
@@ -228,13 +229,25 @@ Configurable props for variant selection:
|
|
|
228
229
|
|
|
229
230
|
#### AccordionTitle.ContentFields
|
|
230
231
|
|
|
231
|
-
Configurable props for title text
|
|
232
|
+
Configurable props for title text:
|
|
232
233
|
|
|
233
234
|
```tsx
|
|
234
235
|
<AccordionTitle.ContentFields
|
|
235
236
|
titleName="title"
|
|
236
237
|
titleLabel="Title"
|
|
237
238
|
titleDefault="Accordion Title"
|
|
239
|
+
/>
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
**Fields:**
|
|
243
|
+
- `title`: TextField for accordion title text
|
|
244
|
+
|
|
245
|
+
#### AccordionTitle.StyleFields
|
|
246
|
+
|
|
247
|
+
Configurable props for icon style:
|
|
248
|
+
|
|
249
|
+
```tsx
|
|
250
|
+
<AccordionTitle.StyleFields
|
|
238
251
|
iconName="icon"
|
|
239
252
|
iconLabel="Icon"
|
|
240
253
|
iconDefault="chevron"
|
|
@@ -242,7 +255,6 @@ Configurable props for title text and icon:
|
|
|
242
255
|
```
|
|
243
256
|
|
|
244
257
|
**Fields:**
|
|
245
|
-
- `title`: TextField for accordion title text
|
|
246
258
|
- `icon`: ChoiceField for selecting icon type (caret, chevron, plus)
|
|
247
259
|
|
|
248
260
|
#### AccordionContent.ContentFields
|
|
@@ -275,11 +287,11 @@ import { AccordionIconType } from '@hubspot/cms-component-library/Accordion/Acco
|
|
|
275
287
|
type FAQModuleProps = {
|
|
276
288
|
style?: {
|
|
277
289
|
variant?: AccordionVariant;
|
|
290
|
+
icon?: AccordionIconType;
|
|
278
291
|
};
|
|
279
292
|
accordionItems?: Array<{
|
|
280
293
|
title?: string;
|
|
281
294
|
content?: string;
|
|
282
|
-
icon?: AccordionIconType;
|
|
283
295
|
}>;
|
|
284
296
|
};
|
|
285
297
|
|
|
@@ -288,10 +300,11 @@ export const Component = ({
|
|
|
288
300
|
accordionItems = [],
|
|
289
301
|
}: FAQModuleProps) => {
|
|
290
302
|
const variant = style?.variant;
|
|
303
|
+
const icon = style?.icon;
|
|
291
304
|
|
|
292
305
|
return (
|
|
293
306
|
<Accordion>
|
|
294
|
-
{accordionItems.map(({ title, content
|
|
307
|
+
{accordionItems.map(({ title, content }, index) => (
|
|
295
308
|
<AccordionItem key={index} variant={variant}>
|
|
296
309
|
<AccordionTitle icon={icon}>{title}</AccordionTitle>
|
|
297
310
|
<AccordionContent>{content}</AccordionContent>
|
|
@@ -326,7 +339,6 @@ import {
|
|
|
326
339
|
const defaultItem = {
|
|
327
340
|
title: 'Accordion Title',
|
|
328
341
|
content: '<p>Accordion content goes here. You can add multiple lines of text.</p>',
|
|
329
|
-
icon: 'chevron',
|
|
330
342
|
};
|
|
331
343
|
|
|
332
344
|
export const fields = (
|
|
@@ -342,6 +354,7 @@ export const fields = (
|
|
|
342
354
|
</RepeatedFieldGroup>
|
|
343
355
|
<FieldGroup label="Style" name="style" tab="STYLE">
|
|
344
356
|
<AccordionItem.StyleFields />
|
|
357
|
+
<AccordionTitle.StyleFields />
|
|
345
358
|
</FieldGroup>
|
|
346
359
|
</ModuleFields>
|
|
347
360
|
);
|
|
@@ -426,7 +439,7 @@ The Accordion component follows accessibility best practices:
|
|
|
426
439
|
- **Gap selection**: Use any valid CSS length value (e.g., '8px', '16px', '24px', '48px') for spacing between items
|
|
427
440
|
- **Dynamic rendering**: Always provide unique `key` props when mapping arrays to AccordionItems
|
|
428
441
|
- **Rich content**: AccordionContent supports any HTML content including lists, paragraphs, images, and nested components
|
|
429
|
-
- **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
|
|
430
443
|
- **CSS Variables**: Override design tokens using CSS variables rather than hardcoding values
|
|
431
444
|
- **Single responsibility**: Keep each accordion item focused on one topic for better UX
|
|
432
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
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { FormField } from '@hubspot/cms-components/fields';
|
|
2
|
+
import { ContentFieldsProps } from './types.js';
|
|
3
|
+
|
|
4
|
+
const ContentFields = ({
|
|
5
|
+
formIdLabel = 'Form',
|
|
6
|
+
formIdName = 'form',
|
|
7
|
+
formIdDefault = {},
|
|
8
|
+
}: ContentFieldsProps) => {
|
|
9
|
+
return (
|
|
10
|
+
<FormField
|
|
11
|
+
label={formIdLabel}
|
|
12
|
+
name={formIdName}
|
|
13
|
+
embedVersions={['v4', 'v3', 'v2']}
|
|
14
|
+
default={formIdDefault}
|
|
15
|
+
/>
|
|
16
|
+
);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export default ContentFields;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// @ts-expect-error -- ?island not typed
|
|
2
|
+
import FormIsland from './islands/FormIsland.js?island';
|
|
3
|
+
// @ts-expect-error -- ?island not typed
|
|
4
|
+
import LegacyFormIsland from './islands/LegacyFormIsland.js?island';
|
|
5
|
+
import { FormProps } from './types.js';
|
|
6
|
+
import { Island } from '@hubspot/cms-components';
|
|
7
|
+
import ContentFields from './ContentFields.js';
|
|
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} />;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
type FormComponentType = typeof FormComponent & {
|
|
22
|
+
ContentFields: typeof ContentFields;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const Form = FormComponent as FormComponentType;
|
|
26
|
+
Form.ContentFields = ContentFields;
|
|
27
|
+
|
|
28
|
+
export default Form;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { FormProps } from '../types.js';
|
|
3
|
+
import { getHubID, getHSEnv } from '@hubspot/cms-components';
|
|
4
|
+
|
|
5
|
+
const getScriptSrc = (portalId: number, env: string) => {
|
|
6
|
+
const host = env === 'qa' ? 'js.hsformsqa.net' : 'js.hsforms.net';
|
|
7
|
+
return `https://${host}/forms/embed/developer/${portalId}.js`;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const FormIsland = ({ formId }: FormProps) => {
|
|
11
|
+
const portalId = getHubID();
|
|
12
|
+
const resolvedEnv = getHSEnv();
|
|
13
|
+
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
if (!formId || !portalId) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const scriptSrc = getScriptSrc(portalId, resolvedEnv);
|
|
20
|
+
const existingScript = document.querySelector(`script[src="${scriptSrc}"]`);
|
|
21
|
+
|
|
22
|
+
if (existingScript) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const script = document.createElement('script');
|
|
27
|
+
script.src = scriptSrc;
|
|
28
|
+
document.head.appendChild(script);
|
|
29
|
+
|
|
30
|
+
return () => {
|
|
31
|
+
script.remove();
|
|
32
|
+
};
|
|
33
|
+
}, [formId, portalId, resolvedEnv]);
|
|
34
|
+
|
|
35
|
+
if (!formId || !portalId) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/*
|
|
40
|
+
<script src="https://js.hsformsqa.net/forms/embed/102771136.js" defer></script>
|
|
41
|
+
<div class="hs-form-frame" data-env="qa" data-region="na1" data-form-id="53e5b258-4526-4012-9274-8bbe23ab2d09" data-portal-id="102771136"></div>
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<div
|
|
46
|
+
className="hs-form-frame"
|
|
47
|
+
data-form-id={formId}
|
|
48
|
+
data-portal-id={portalId}
|
|
49
|
+
data-env={resolvedEnv}
|
|
50
|
+
/>
|
|
51
|
+
);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export default FormIsland;
|
|
@@ -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;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { FormFieldDefaults } from '@hubspot/cms-components/fields';
|
|
2
|
+
|
|
3
|
+
export type FormProps = FormPropsWithField | FormPropsWithoutField;
|
|
4
|
+
|
|
5
|
+
export type FormPropsWithoutField = {
|
|
6
|
+
formField?: never;
|
|
7
|
+
formId: string;
|
|
8
|
+
formVersion: 'v4' | 'v3' | 'v2' | '';
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export type FormPropsWithField = {
|
|
12
|
+
formField: typeof FormFieldDefaults;
|
|
13
|
+
formId?: never;
|
|
14
|
+
formVersion?: never;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type ContentFieldsProps = {
|
|
18
|
+
formIdLabel?: string;
|
|
19
|
+
formIdName?: string;
|
|
20
|
+
formIdDefault?: typeof FormFieldDefaults;
|
|
21
|
+
};
|
|
@@ -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": {
|