@hubspot/cms-component-library 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/componentLibrary/Accordion/AccordionContent/ContentFields.tsx +5 -3
- package/components/componentLibrary/Accordion/AccordionItem/StyleFields.tsx +5 -3
- package/components/componentLibrary/Accordion/AccordionItem/index.module.scss +2 -2
- package/components/componentLibrary/Accordion/AccordionItem/index.tsx +3 -3
- package/components/componentLibrary/Accordion/AccordionTitle/ContentFields.tsx +5 -3
- package/components/componentLibrary/Accordion/AccordionTitle/index.module.scss +2 -2
- package/components/componentLibrary/Accordion/stories/Accordion.stories.tsx +80 -1
- package/components/componentLibrary/Accordion/stories/AccordionDecorator.tsx +14 -14
- package/components/componentLibrary/Button/ContentFields.tsx +5 -3
- package/components/componentLibrary/Button/StyleFields.tsx +26 -4
- package/components/componentLibrary/Button/index.module.scss +22 -14
- package/components/componentLibrary/Button/index.tsx +6 -6
- package/components/componentLibrary/Button/llm.txt +5 -1
- package/components/componentLibrary/Button/stories/Button.AsButton.stories.tsx +30 -1
- package/components/componentLibrary/Button/stories/Button.AsLink.stories.tsx +38 -1
- package/components/componentLibrary/Button/stories/ButtonDecorator.tsx +1 -1
- package/components/componentLibrary/Button/types.ts +6 -1
- package/components/componentLibrary/Card/StyleFields.tsx +5 -3
- package/components/componentLibrary/Card/stories/Card.stories.tsx +46 -1
- package/components/componentLibrary/Card/stories/CardDecorator.tsx +1 -1
- package/components/componentLibrary/Divider/ContentFields.tsx +5 -3
- package/components/componentLibrary/Divider/StyleFields.tsx +5 -3
- package/components/componentLibrary/Divider/index.module.scss +6 -6
- package/components/componentLibrary/Divider/index.tsx +7 -3
- package/components/componentLibrary/Divider/stories/Divider.stories.tsx +44 -50
- package/components/componentLibrary/Divider/stories/{DividerDecorator.module.css → DividerDecorator.module.scss} +5 -4
- package/components/componentLibrary/Divider/stories/DividerDecorator.tsx +1 -1
- package/components/componentLibrary/Divider/types.ts +3 -1
- package/components/componentLibrary/Drawer/hooks/index.tsx +13 -0
- package/components/componentLibrary/Drawer/index.module.scss +94 -0
- package/components/componentLibrary/Drawer/index.tsx +131 -0
- package/components/componentLibrary/Drawer/llm.txt +416 -0
- package/components/componentLibrary/Drawer/stories/Drawer.stories.tsx +512 -0
- package/components/componentLibrary/Drawer/stories/DrawerDecorator.module.scss +8 -0
- package/components/componentLibrary/Drawer/stories/DrawerDecorator.tsx +18 -0
- package/components/componentLibrary/Drawer/types.ts +25 -0
- package/components/componentLibrary/Flex/stories/FlexDecorator.tsx +1 -1
- package/components/componentLibrary/Flex/types.ts +3 -1
- package/components/componentLibrary/Grid/stories/Grid.stories.tsx +454 -152
- package/components/componentLibrary/Grid/stories/GridDecorator.tsx +2 -2
- package/components/componentLibrary/Heading/ContentFields.tsx +5 -3
- package/components/componentLibrary/Heading/StyleFields.tsx +11 -9
- package/components/componentLibrary/Heading/index.tsx +3 -3
- package/components/componentLibrary/Heading/llm.txt +8 -8
- package/components/componentLibrary/Heading/stories/Heading.stories.tsx +3 -3
- package/components/componentLibrary/Heading/stories/HeadingDecorator.tsx +1 -1
- package/components/componentLibrary/Heading/types.ts +4 -4
- package/components/componentLibrary/Icon/ContentFields.tsx +5 -3
- package/components/componentLibrary/Icon/stories/Icon.stories.tsx +1 -1
- package/components/componentLibrary/Icon/stories/IconDecorator.tsx +1 -1
- package/components/componentLibrary/Image/ContentFields.tsx +5 -3
- package/components/componentLibrary/Image/index.tsx +4 -4
- package/components/componentLibrary/Image/llm.txt +17 -17
- package/components/componentLibrary/Image/stories/Image.stories.tsx +61 -18
- package/components/componentLibrary/Image/stories/ImageDecorator.tsx +1 -1
- package/components/componentLibrary/Image/types.ts +2 -2
- package/components/componentLibrary/LanguageSwitcher/ContentFields.tsx +18 -0
- package/components/componentLibrary/LanguageSwitcher/LanguageOptions.module.scss +37 -0
- package/components/componentLibrary/LanguageSwitcher/LanguageOptions.tsx +65 -0
- package/components/componentLibrary/LanguageSwitcher/StyleFields.tsx +48 -0
- package/components/componentLibrary/LanguageSwitcher/_dummyData.tsx +247 -0
- package/components/componentLibrary/LanguageSwitcher/assets/Globe.tsx +16 -0
- package/components/componentLibrary/LanguageSwitcher/index.module.scss +58 -0
- package/components/componentLibrary/LanguageSwitcher/index.tsx +125 -0
- package/components/componentLibrary/LanguageSwitcher/llm.txt +380 -0
- package/components/componentLibrary/LanguageSwitcher/stories/LanguageSwitcher.stories.tsx +349 -0
- package/components/componentLibrary/LanguageSwitcher/stories/LanguageSwitcherDecorator.module.scss +5 -0
- package/components/componentLibrary/LanguageSwitcher/stories/LanguageSwitcherDecorator.tsx +8 -0
- package/components/componentLibrary/LanguageSwitcher/types.ts +48 -0
- package/components/componentLibrary/LanguageSwitcher/utils.tsx +38 -0
- package/components/componentLibrary/Link/ContentFields.tsx +5 -3
- package/components/componentLibrary/Link/StyleFields.tsx +5 -3
- package/components/componentLibrary/Link/index.module.scss +10 -0
- package/components/componentLibrary/Link/index.tsx +24 -14
- package/components/componentLibrary/Link/stories/Link.stories.tsx +35 -5
- package/components/componentLibrary/Link/stories/LinkDecorator.tsx +11 -1
- package/components/componentLibrary/Link/types.ts +22 -13
- package/components/componentLibrary/List/ContentFields.tsx +5 -3
- package/components/componentLibrary/List/ListItem/ContentFields.tsx +6 -17
- package/components/componentLibrary/List/ListItem/index.module.scss +1 -13
- package/components/componentLibrary/List/ListItem/index.tsx +3 -30
- package/components/componentLibrary/List/ListItem/types.ts +1 -16
- package/components/componentLibrary/List/StyleFields.tsx +15 -18
- package/components/componentLibrary/List/index.module.scss +3 -0
- package/components/componentLibrary/List/index.tsx +5 -2
- package/components/componentLibrary/List/llm.txt +73 -103
- package/components/componentLibrary/List/stories/List.stories.tsx +56 -80
- package/components/componentLibrary/List/stories/ListDecorator.tsx +3 -6
- package/components/componentLibrary/List/types.ts +1 -3
- package/components/componentLibrary/Logo/_dummyLogoData.ts +12 -0
- package/components/componentLibrary/Logo/assets/hubspot-logo.png +0 -0
- package/components/componentLibrary/Logo/index.module.scss +22 -0
- package/components/componentLibrary/Logo/index.tsx +73 -0
- package/components/componentLibrary/Logo/llm.txt +262 -0
- package/components/componentLibrary/Logo/stories/Logo.stories.tsx +88 -0
- package/components/componentLibrary/Logo/stories/LogoDecorator.module.scss +10 -0
- package/components/componentLibrary/Logo/stories/LogoDecorator.tsx +8 -0
- package/components/componentLibrary/Logo/types.tsx +16 -0
- package/components/componentLibrary/Menu/ContentFields.tsx +16 -0
- package/components/componentLibrary/Menu/MenuItem/Chevron/index.module.scss +6 -0
- package/components/componentLibrary/Menu/MenuItem/Chevron/index.tsx +17 -0
- package/components/componentLibrary/Menu/MenuItem/index.module.scss +7 -0
- package/components/componentLibrary/Menu/MenuItem/index.tsx +266 -0
- package/components/componentLibrary/Menu/MenuItem/types.ts +17 -0
- package/components/componentLibrary/Menu/NavigationMenu/ContentFields.tsx +20 -0
- package/components/componentLibrary/Menu/NavigationMenu/index.tsx +18 -0
- package/components/componentLibrary/Menu/NavigationMenu/islands/NavigationMenuIsland.tsx +95 -0
- package/components/componentLibrary/Menu/NavigationMenu/islands/index.module.scss +100 -0
- package/components/componentLibrary/Menu/NavigationMenu/islands/types.ts +19 -0
- package/components/componentLibrary/Menu/NavigationMenu/llm.txt +197 -0
- package/components/componentLibrary/Menu/NavigationMenu/stories/NavigationMenu.stories.tsx +286 -0
- package/components/componentLibrary/Menu/NavigationMenu/stories/NavigationMenuDecorator.module.scss +15 -0
- package/components/componentLibrary/Menu/NavigationMenu/stories/NavigationMenuDecorator.tsx +12 -0
- package/components/componentLibrary/Menu/NavigationMenu/types.ts +3 -0
- package/components/componentLibrary/Menu/VerticalMenu/ContentFields.tsx +20 -0
- package/components/componentLibrary/Menu/VerticalMenu/index.tsx +18 -0
- package/components/componentLibrary/Menu/VerticalMenu/islands/index.module.scss +53 -0
- package/components/componentLibrary/Menu/VerticalMenu/islands/verticalMenuIsland.tsx +78 -0
- package/components/componentLibrary/Menu/VerticalMenu/llm.txt +177 -0
- package/components/componentLibrary/Menu/VerticalMenu/stories/VerticalMenu.stories.tsx +242 -0
- package/components/componentLibrary/Menu/VerticalMenu/stories/VerticalMenuDecorator.module.scss +19 -0
- package/components/componentLibrary/Menu/VerticalMenu/stories/VerticalMenuDecorator.tsx +12 -0
- package/components/componentLibrary/Menu/VerticalMenu/types.ts +21 -0
- package/components/componentLibrary/Menu/_dummyMenuData.js +1346 -0
- package/components/componentLibrary/Menu/types.ts +56 -0
- package/components/componentLibrary/Menu/utils/transformMenuData.ts +11 -0
- package/components/componentLibrary/_patterns/README.md +15 -17
- package/components/componentLibrary/_patterns/checklist-and-examples.md +17 -17
- package/components/componentLibrary/_patterns/component-structure.md +21 -23
- package/components/componentLibrary/_patterns/css-patterns.md +170 -18
- package/components/componentLibrary/_patterns/field-patterns.md +97 -27
- package/components/componentLibrary/_patterns/function-declaration-patterns.md +281 -0
- package/components/componentLibrary/_patterns/llm-txt.template.md +4 -2
- package/components/componentLibrary/_patterns/prop-naming-patterns.md +208 -0
- package/components/componentLibrary/_patterns/storybook-patterns.md +25 -8
- package/components/componentLibrary/_patterns/typescript-patterns.md +6 -3
- package/package.json +6 -3
- /package/components/componentLibrary/Button/stories/{ButtonDecorator.module.css → ButtonDecorator.module.scss} +0 -0
- /package/components/componentLibrary/Card/stories/{CardDecorator.module.css → CardDecorator.module.scss} +0 -0
- /package/components/componentLibrary/Flex/stories/{FlexDecorator.module.css → FlexDecorator.module.scss} +0 -0
- /package/components/componentLibrary/Grid/stories/{GridDecorator.module.css → GridDecorator.module.scss} +0 -0
- /package/components/componentLibrary/Heading/stories/{HeadingDecorator.module.css → HeadingDecorator.module.scss} +0 -0
- /package/components/componentLibrary/Icon/stories/{IconDecorator.module.css → IconDecorator.module.scss} +0 -0
- /package/components/componentLibrary/Image/stories/{ImageDecorator.module.css → ImageDecorator.module.scss} +0 -0
- /package/components/componentLibrary/Image/stories/assets/{catSmile.jpg → cat-smile.jpg} +0 -0
|
@@ -27,12 +27,13 @@ export const Default: Story = {
|
|
|
27
27
|
listType: 'unordered',
|
|
28
28
|
variant: 'primary',
|
|
29
29
|
gap: '0',
|
|
30
|
+
showMarker: true,
|
|
30
31
|
},
|
|
31
32
|
render: args => (
|
|
32
33
|
<List {...args}>
|
|
33
|
-
<ListItem
|
|
34
|
-
<ListItem
|
|
35
|
-
<ListItem
|
|
34
|
+
<ListItem>First item</ListItem>
|
|
35
|
+
<ListItem>Second item</ListItem>
|
|
36
|
+
<ListItem>Third item</ListItem>
|
|
36
37
|
</List>
|
|
37
38
|
),
|
|
38
39
|
};
|
|
@@ -44,9 +45,9 @@ export const OrderedList: Story = {
|
|
|
44
45
|
<SBContainer addBackground>
|
|
45
46
|
<h4>Numbered List</h4>
|
|
46
47
|
<List listType="ordered" variant="primary">
|
|
47
|
-
<ListItem
|
|
48
|
-
<ListItem
|
|
49
|
-
<ListItem
|
|
48
|
+
<ListItem>First step</ListItem>
|
|
49
|
+
<ListItem>Second step</ListItem>
|
|
50
|
+
<ListItem>Third step</ListItem>
|
|
50
51
|
</List>
|
|
51
52
|
</SBContainer>
|
|
52
53
|
</SBContainer>
|
|
@@ -60,9 +61,9 @@ export const UnorderedList: Story = {
|
|
|
60
61
|
<SBContainer addBackground>
|
|
61
62
|
<h4>Bulleted List</h4>
|
|
62
63
|
<List listType="unordered" variant="primary">
|
|
63
|
-
<ListItem
|
|
64
|
-
<ListItem
|
|
65
|
-
<ListItem
|
|
64
|
+
<ListItem>First item</ListItem>
|
|
65
|
+
<ListItem>Second item</ListItem>
|
|
66
|
+
<ListItem>Third item</ListItem>
|
|
66
67
|
</List>
|
|
67
68
|
</SBContainer>
|
|
68
69
|
</SBContainer>
|
|
@@ -93,14 +94,7 @@ export const DynamicRendering: Story = {
|
|
|
93
94
|
<h4>Feature List (Mapped from Array)</h4>
|
|
94
95
|
<List listType="unordered" variant="primary">
|
|
95
96
|
{features.map((item, index) => (
|
|
96
|
-
<ListItem
|
|
97
|
-
key={index}
|
|
98
|
-
showIcon={false}
|
|
99
|
-
index={index}
|
|
100
|
-
itemsFieldPath="features"
|
|
101
|
-
>
|
|
102
|
-
{item.text}
|
|
103
|
-
</ListItem>
|
|
97
|
+
<ListItem key={index}>{item.text}</ListItem>
|
|
104
98
|
))}
|
|
105
99
|
</List>
|
|
106
100
|
</SBContainer>
|
|
@@ -109,14 +103,7 @@ export const DynamicRendering: Story = {
|
|
|
109
103
|
<h4>Step-by-Step Instructions</h4>
|
|
110
104
|
<List listType="ordered" variant="primary">
|
|
111
105
|
{steps.map((item, index) => (
|
|
112
|
-
<ListItem
|
|
113
|
-
key={index}
|
|
114
|
-
showIcon={false}
|
|
115
|
-
index={index}
|
|
116
|
-
itemsFieldPath="steps"
|
|
117
|
-
>
|
|
118
|
-
{item.text}
|
|
119
|
-
</ListItem>
|
|
106
|
+
<ListItem key={index}>{item.text}</ListItem>
|
|
120
107
|
))}
|
|
121
108
|
</List>
|
|
122
109
|
</SBContainer>
|
|
@@ -125,40 +112,24 @@ export const DynamicRendering: Story = {
|
|
|
125
112
|
},
|
|
126
113
|
};
|
|
127
114
|
|
|
128
|
-
export const
|
|
129
|
-
name: 'List
|
|
115
|
+
export const NoMarkers: Story = {
|
|
116
|
+
name: 'List without Markers',
|
|
130
117
|
render: () => (
|
|
131
118
|
<SBContainer flex direction="column" gap="large">
|
|
132
119
|
<SBContainer addBackground>
|
|
133
|
-
<h4>List
|
|
134
|
-
<List listType="unordered" variant="primary">
|
|
135
|
-
<ListItem
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
</ListItem>
|
|
144
|
-
<ListItem
|
|
145
|
-
|
|
146
|
-
iconName="icon"
|
|
147
|
-
iconSize={16}
|
|
148
|
-
itemsFieldPath="items"
|
|
149
|
-
index={1}
|
|
150
|
-
>
|
|
151
|
-
Another item with icon
|
|
152
|
-
</ListItem>
|
|
153
|
-
<ListItem
|
|
154
|
-
showIcon={true}
|
|
155
|
-
iconName="icon"
|
|
156
|
-
iconSize={16}
|
|
157
|
-
itemsFieldPath="items"
|
|
158
|
-
index={2}
|
|
159
|
-
>
|
|
160
|
-
Third item with icon
|
|
161
|
-
</ListItem>
|
|
120
|
+
<h4>Unordered List (No Markers)</h4>
|
|
121
|
+
<List listType="unordered" variant="primary" showMarker={false}>
|
|
122
|
+
<ListItem>First item without bullet</ListItem>
|
|
123
|
+
<ListItem>Second item without bullet</ListItem>
|
|
124
|
+
<ListItem>Third item without bullet</ListItem>
|
|
125
|
+
</List>
|
|
126
|
+
</SBContainer>
|
|
127
|
+
<SBContainer addBackground>
|
|
128
|
+
<h4>Ordered List (No Markers)</h4>
|
|
129
|
+
<List listType="ordered" variant="primary" showMarker={false}>
|
|
130
|
+
<ListItem>First step without number</ListItem>
|
|
131
|
+
<ListItem>Second step without number</ListItem>
|
|
132
|
+
<ListItem>Third step without number</ListItem>
|
|
162
133
|
</List>
|
|
163
134
|
</SBContainer>
|
|
164
135
|
</SBContainer>
|
|
@@ -172,18 +143,18 @@ export const Spacing: Story = {
|
|
|
172
143
|
<SBContainer addBackground>
|
|
173
144
|
<h4>Default Spacing (gap 0 but inherits li styles)</h4>
|
|
174
145
|
<List listType="unordered" variant="primary">
|
|
175
|
-
<ListItem
|
|
176
|
-
<ListItem
|
|
177
|
-
<ListItem
|
|
146
|
+
<ListItem>Default item one</ListItem>
|
|
147
|
+
<ListItem>Default item two</ListItem>
|
|
148
|
+
<ListItem>Default item three</ListItem>
|
|
178
149
|
</List>
|
|
179
150
|
</SBContainer>
|
|
180
151
|
|
|
181
152
|
<SBContainer addBackground>
|
|
182
153
|
<h4>Generous Spacing (1rem)</h4>
|
|
183
154
|
<List listType="unordered" variant="primary" gap="1rem">
|
|
184
|
-
<ListItem
|
|
185
|
-
<ListItem
|
|
186
|
-
<ListItem
|
|
155
|
+
<ListItem>Generous item one</ListItem>
|
|
156
|
+
<ListItem>Generous item two</ListItem>
|
|
157
|
+
<ListItem>Generous item three</ListItem>
|
|
187
158
|
</List>
|
|
188
159
|
</SBContainer>
|
|
189
160
|
</SBContainer>
|
|
@@ -197,20 +168,20 @@ export const NestedLists: Story = {
|
|
|
197
168
|
<SBContainer addBackground>
|
|
198
169
|
<h4>Nested Unordered Lists</h4>
|
|
199
170
|
<List listType="unordered" variant="primary">
|
|
200
|
-
<ListItem
|
|
171
|
+
<ListItem>Parent item one</ListItem>
|
|
201
172
|
<li>
|
|
202
173
|
Parent item two
|
|
203
174
|
<List listType="unordered" variant="primary">
|
|
204
|
-
<ListItem
|
|
205
|
-
<ListItem
|
|
175
|
+
<ListItem>Child item one</ListItem>
|
|
176
|
+
<ListItem>Child item two</ListItem>
|
|
206
177
|
</List>
|
|
207
178
|
</li>
|
|
208
|
-
<ListItem
|
|
179
|
+
<ListItem>Parent item three</ListItem>
|
|
209
180
|
<li>
|
|
210
181
|
Parent item four
|
|
211
182
|
<List listType="unordered" variant="primary">
|
|
212
|
-
<ListItem
|
|
213
|
-
<ListItem
|
|
183
|
+
<ListItem>Child item one</ListItem>
|
|
184
|
+
<ListItem>Child item two</ListItem>
|
|
214
185
|
</List>
|
|
215
186
|
</li>
|
|
216
187
|
</List>
|
|
@@ -219,21 +190,21 @@ export const NestedLists: Story = {
|
|
|
219
190
|
<SBContainer addBackground>
|
|
220
191
|
<h4>Nested Ordered Lists</h4>
|
|
221
192
|
<List listType="ordered" variant="primary">
|
|
222
|
-
<ListItem
|
|
193
|
+
<ListItem>Step one</ListItem>
|
|
223
194
|
<li>
|
|
224
195
|
Step two
|
|
225
196
|
<List listType="ordered" variant="secondary" gap="0.25rem">
|
|
226
|
-
<ListItem
|
|
227
|
-
<ListItem
|
|
197
|
+
<ListItem>Sub-step A</ListItem>
|
|
198
|
+
<ListItem>Sub-step B</ListItem>
|
|
228
199
|
</List>
|
|
229
200
|
</li>
|
|
230
|
-
<ListItem
|
|
231
|
-
<ListItem
|
|
201
|
+
<ListItem>Step three</ListItem>
|
|
202
|
+
<ListItem>Step four</ListItem>
|
|
232
203
|
<li>
|
|
233
204
|
Step five
|
|
234
205
|
<List listType="ordered" variant="secondary" gap="0.25rem">
|
|
235
|
-
<ListItem
|
|
236
|
-
<ListItem
|
|
206
|
+
<ListItem>Sub-step A</ListItem>
|
|
207
|
+
<ListItem>Sub-step B</ListItem>
|
|
237
208
|
</List>
|
|
238
209
|
</li>
|
|
239
210
|
</List>
|
|
@@ -249,11 +220,16 @@ export const EdgeCases: Story = {
|
|
|
249
220
|
<SBContainer addBackground>
|
|
250
221
|
<h4>Long Text Content</h4>
|
|
251
222
|
<List listType="unordered" variant="primary">
|
|
252
|
-
<ListItem
|
|
253
|
-
This is a very long list item that demonstrates how the component
|
|
223
|
+
<ListItem>
|
|
224
|
+
This is a very long list item that demonstrates how the component
|
|
225
|
+
handles overflow and wrapping of content across multiple lines when
|
|
226
|
+
the text becomes too long to fit on a single line.
|
|
254
227
|
</ListItem>
|
|
255
|
-
<ListItem
|
|
256
|
-
Another extremely long list item with lots of text to show the
|
|
228
|
+
<ListItem>
|
|
229
|
+
Another extremely long list item with lots of text to show the
|
|
230
|
+
wrapping behavior and how the component maintains proper spacing and
|
|
231
|
+
alignment even when the content spans multiple lines in the list
|
|
232
|
+
item element.
|
|
257
233
|
</ListItem>
|
|
258
234
|
</List>
|
|
259
235
|
</SBContainer>
|
|
@@ -266,7 +242,7 @@ export const EdgeCases: Story = {
|
|
|
266
242
|
<SBContainer addBackground>
|
|
267
243
|
<h4>Single Item</h4>
|
|
268
244
|
<List listType="unordered" variant="primary">
|
|
269
|
-
<ListItem
|
|
245
|
+
<ListItem>Only one item</ListItem>
|
|
270
246
|
</List>
|
|
271
247
|
</SBContainer>
|
|
272
248
|
</SBContainer>
|
|
@@ -2,12 +2,9 @@ import type { Decorator } from '@storybook/react';
|
|
|
2
2
|
import type { CSSVariables } from '../../utils/types.js';
|
|
3
3
|
|
|
4
4
|
const defaultListStyles: CSSVariables = {
|
|
5
|
-
'--hscl-listItem-
|
|
6
|
-
'--hscl-listItem-
|
|
7
|
-
'--hscl-listItem-
|
|
8
|
-
'--hscl-listItem-iconColor-primary': '#33475b',
|
|
9
|
-
'--hscl-listItem-iconColor-secondary': '#516f90',
|
|
10
|
-
'--hscl-listItem-iconColor-tertiary': '#7c98b6',
|
|
5
|
+
'--hscl-listItem-color-primary': '#33475b',
|
|
6
|
+
'--hscl-listItem-color-secondary': '#516f90',
|
|
7
|
+
'--hscl-listItem-color-tertiary': '#7c98b6',
|
|
11
8
|
};
|
|
12
9
|
|
|
13
10
|
export const withListStyles: Decorator = Story => (
|
|
@@ -10,6 +10,7 @@ export type ListProps = {
|
|
|
10
10
|
listType?: ListType;
|
|
11
11
|
variant?: ListVariant;
|
|
12
12
|
gap?: GapValue;
|
|
13
|
+
showMarker?: boolean;
|
|
13
14
|
className?: string;
|
|
14
15
|
style?: CSSVariables;
|
|
15
16
|
children?: React.ReactNode;
|
|
@@ -25,7 +26,4 @@ export type StyleFieldsProps = {
|
|
|
25
26
|
variantName?: string;
|
|
26
27
|
variantLabel?: string;
|
|
27
28
|
variantDefault?: ListVariant;
|
|
28
|
-
gapName?: string;
|
|
29
|
-
gapLabel?: string;
|
|
30
|
-
gapDefault?: GapValue;
|
|
31
29
|
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { LogoData } from './types.js';
|
|
2
|
+
import hubspotLogo from './assets/hubspot-logo.png';
|
|
3
|
+
|
|
4
|
+
export const dummyLogoData: { logo: LogoData } = {
|
|
5
|
+
logo: {
|
|
6
|
+
src: hubspotLogo,
|
|
7
|
+
alt: 'HubSpot Logo',
|
|
8
|
+
width: 150,
|
|
9
|
+
height: 50,
|
|
10
|
+
link: 'https://www.hubspot.com',
|
|
11
|
+
},
|
|
12
|
+
};
|
|
Binary file
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
.logo {
|
|
2
|
+
display: var(--hscl-logo-display, inline-block);
|
|
3
|
+
text-decoration: none;
|
|
4
|
+
inline-size: fit-content;
|
|
5
|
+
max-inline-size: var(--hscl-logo-maxWidth, 100%);
|
|
6
|
+
max-block-size: var(--hscl-logo-maxHeight, none);
|
|
7
|
+
|
|
8
|
+
&:hover {
|
|
9
|
+
cursor: var(--hscl-logo-cursor-hover, default);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
&.link,
|
|
13
|
+
&.link:hover {
|
|
14
|
+
cursor: var(--hscl-logo-cursor-hover, pointer);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.logoImage {
|
|
18
|
+
display: block;
|
|
19
|
+
max-inline-size: var(--hscl-logo-maxWidth, 100%);
|
|
20
|
+
max-block-size: var(--hscl-logo-maxHeight, none);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
import { useBrandSettings } from '@hubspot/cms-components';
|
|
3
|
+
import { dummyLogoData } from './_dummyLogoData.js';
|
|
4
|
+
import { LogoData, LogoProps } from './types.js';
|
|
5
|
+
import styles from './index.module.scss';
|
|
6
|
+
import cx from '../utils/classname.js';
|
|
7
|
+
import Link from '../Link/index.js';
|
|
8
|
+
import Image from '../Image/index.js';
|
|
9
|
+
import type { CSSVariables } from '../utils/types.js';
|
|
10
|
+
|
|
11
|
+
export const Logo = ({
|
|
12
|
+
useDummyLogo = false,
|
|
13
|
+
className = '',
|
|
14
|
+
style = {},
|
|
15
|
+
loading = 'eager',
|
|
16
|
+
maxWidth,
|
|
17
|
+
maxHeight,
|
|
18
|
+
}: LogoProps) => {
|
|
19
|
+
const { primaryLogo } = useBrandSettings() ?? {};
|
|
20
|
+
|
|
21
|
+
const logo: LogoData | undefined = useMemo(() => {
|
|
22
|
+
return useDummyLogo ? dummyLogoData.logo : primaryLogo;
|
|
23
|
+
}, [useDummyLogo, primaryLogo]);
|
|
24
|
+
|
|
25
|
+
if (!logo?.src) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const hasOnlyMaxHeight = maxHeight && !maxWidth;
|
|
30
|
+
|
|
31
|
+
const defaultClasses = styles.logo;
|
|
32
|
+
const combinedClasses = cx(defaultClasses, className);
|
|
33
|
+
const imageClasses = styles.logoImage;
|
|
34
|
+
|
|
35
|
+
const wrapperStyle: CSSVariables = {
|
|
36
|
+
...(maxWidth && { '--hscl-logo-maxWidth': maxWidth }),
|
|
37
|
+
...(maxHeight && { '--hscl-logo-maxHeight': maxHeight }),
|
|
38
|
+
...(hasOnlyMaxHeight && { '--hscl-logo-maxWidth': 'fit-content' }),
|
|
39
|
+
...style,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const imageStyle: CSSVariables = {
|
|
43
|
+
...(hasOnlyMaxHeight && {
|
|
44
|
+
inlineSize: 'auto',
|
|
45
|
+
blockSize: 'auto',
|
|
46
|
+
}),
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const content = (
|
|
50
|
+
<Image
|
|
51
|
+
src={logo.src}
|
|
52
|
+
alt={logo.alt ?? 'Logo'}
|
|
53
|
+
loading={loading}
|
|
54
|
+
width={logo.width}
|
|
55
|
+
height={logo.height}
|
|
56
|
+
className={imageClasses}
|
|
57
|
+
style={imageStyle}
|
|
58
|
+
responsive={true}
|
|
59
|
+
/>
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
return logo.link ? (
|
|
63
|
+
<Link href={logo.link} className={combinedClasses} style={wrapperStyle}>
|
|
64
|
+
{content}
|
|
65
|
+
</Link>
|
|
66
|
+
) : (
|
|
67
|
+
<div className={combinedClasses} style={wrapperStyle}>
|
|
68
|
+
{content}
|
|
69
|
+
</div>
|
|
70
|
+
);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export default Logo;
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
# Logo Component
|
|
2
|
+
|
|
3
|
+
A responsive logo component that displays brand logos from HubSpot brand settings with optional link wrapping, size constraints, and loading optimization.
|
|
4
|
+
|
|
5
|
+
## Import path
|
|
6
|
+
```tsx
|
|
7
|
+
import Logo from '@hubspot/cms-component-library/Logo';
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## Purpose
|
|
11
|
+
|
|
12
|
+
The Logo component automatically integrates with HubSpot's brand settings to display the primary brand logo. It handles responsive image rendering, optional link wrapping for navigation, size constraints for different layout contexts (navigation bars, footers, headers), and loading optimization. This component eliminates the need to manually configure logo images across modules and ensures brand consistency throughout the site.
|
|
13
|
+
|
|
14
|
+
## Component Structure
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
Logo/
|
|
18
|
+
├── index.tsx # Main component with render logic
|
|
19
|
+
├── types.tsx # TypeScript type definitions
|
|
20
|
+
├── index.module.scss # CSS module with design tokens
|
|
21
|
+
├── _dummyLogoData.ts # Test data for development
|
|
22
|
+
└── assets/
|
|
23
|
+
└── hubspot-logo.png # Default dummy logo asset
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Props
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
{
|
|
30
|
+
useDummyLogo?: boolean; // Use test logo instead of brand settings (development only)
|
|
31
|
+
className?: string; // Additional CSS classes
|
|
32
|
+
style?: React.CSSProperties; // Inline styles (including CSS variables)
|
|
33
|
+
maxWidth?: string; // Maximum width constraint (e.g., "150px", "10rem", "50%")
|
|
34
|
+
maxHeight?: string; // Maximum height constraint (e.g., "50px", "5rem", "10vh")
|
|
35
|
+
loading?: 'lazy' | 'eager' | 'disabled'; // Image loading behavior (defaults to 'eager')
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**Note:** The Logo component does not accept `width` or `height` props directly. The intrinsic dimensions come from the brand settings logo data (`LogoData.width` and `LogoData.height`), which are used to set the image's intrinsic size via the `width` and `height` attributes on the `<img>` element. If no `maxWidth` or `maxHeight` props are provided, the logo will display at its intrinsic size from brand settings (constrained by responsive CSS to a maximum of 100% of the container width). Use `maxWidth` and `maxHeight` to constrain the display size while maintaining the logo's aspect ratio. Both props accept any valid CSS dimension value (pixels, rem, percentages, viewport units, etc.).
|
|
40
|
+
|
|
41
|
+
**Brand Settings Data Structure:**
|
|
42
|
+
The component uses `useBrandSettings()` which returns logo data in this format:
|
|
43
|
+
```tsx
|
|
44
|
+
{
|
|
45
|
+
src: string; // Logo image URL
|
|
46
|
+
alt?: string; // Alt text for accessibility
|
|
47
|
+
width?: number; // Intrinsic width in pixels (sets image width attribute; establishes initial size when no maxWidth/maxHeight provided)
|
|
48
|
+
height?: number; // Intrinsic height in pixels (sets image height attribute; establishes initial size when no maxWidth/maxHeight provided)
|
|
49
|
+
link?: string; // Optional URL for logo link
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Usage Examples
|
|
54
|
+
|
|
55
|
+
### Basic Logo
|
|
56
|
+
|
|
57
|
+
```tsx
|
|
58
|
+
import Logo from '@hubspot/cms-component-library/Logo';
|
|
59
|
+
|
|
60
|
+
<Logo />
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Logo in Navigation Bar
|
|
64
|
+
|
|
65
|
+
When you need to constrain logo size in a navigation header:
|
|
66
|
+
|
|
67
|
+
```tsx
|
|
68
|
+
<nav>
|
|
69
|
+
<Logo maxWidth="120px" />
|
|
70
|
+
{/* Navigation items */}
|
|
71
|
+
</nav>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Logo with Lazy Loading
|
|
75
|
+
|
|
76
|
+
For logos that appear below the fold:
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
<Logo
|
|
80
|
+
loading="lazy"
|
|
81
|
+
maxWidth="150px"
|
|
82
|
+
/>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Height-Only Constraint
|
|
86
|
+
|
|
87
|
+
When you only need to constrain the height (e.g., in a navigation bar with fixed height), use `maxHeight` alone. The component automatically maintains the aspect ratio:
|
|
88
|
+
|
|
89
|
+
```tsx
|
|
90
|
+
<nav>
|
|
91
|
+
<Logo maxHeight="50px" />
|
|
92
|
+
{/* Navigation items */}
|
|
93
|
+
</nav>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**Special Behavior:** When only `maxHeight` is provided (without `maxWidth`), the component automatically:
|
|
97
|
+
- Sets `maxWidth` to `fit-content` via the `--hscl-logo-maxWidth` CSS variable
|
|
98
|
+
- Sets the image's `inlineSize` and `blockSize` to `auto`
|
|
99
|
+
- This ensures the logo maintains its natural aspect ratio while respecting the height constraint, allowing the width to scale proportionally
|
|
100
|
+
|
|
101
|
+
### Custom Sizing
|
|
102
|
+
|
|
103
|
+
Constrain logo size using both maxWidth and maxHeight while maintaining aspect ratio:
|
|
104
|
+
|
|
105
|
+
```tsx
|
|
106
|
+
<Logo
|
|
107
|
+
maxWidth="250px"
|
|
108
|
+
maxHeight="80px"
|
|
109
|
+
/>
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Local Development Testing
|
|
113
|
+
|
|
114
|
+
When testing locally on the dev server where brand settings aren't available, use test data:
|
|
115
|
+
|
|
116
|
+
```tsx
|
|
117
|
+
<Logo useDummyLogo={true} maxWidth="150px" />
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## HubSpot CMS Integration
|
|
121
|
+
|
|
122
|
+
### Brand Settings Integration
|
|
123
|
+
|
|
124
|
+
The Logo component automatically retrieves logo data from HubSpot brand settings using the `useBrandSettings()` hook from `@hubspot/cms-components`. While this hook provides access to multiple logos, this component specifically uses only the primary logo configured in the HubSpot brand kit settings.
|
|
125
|
+
|
|
126
|
+
### Module Usage Example
|
|
127
|
+
|
|
128
|
+
```tsx
|
|
129
|
+
import Logo from '@hubspot/cms-component-library/Logo';
|
|
130
|
+
|
|
131
|
+
export default function HeaderModule({ fieldValues }) {
|
|
132
|
+
return (
|
|
133
|
+
<header>
|
|
134
|
+
<Logo
|
|
135
|
+
maxWidth={fieldValues.logoMaxWidth}
|
|
136
|
+
loading="eager"
|
|
137
|
+
/>
|
|
138
|
+
<nav>{/* Navigation items */}</nav>
|
|
139
|
+
</header>
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Field Configuration
|
|
145
|
+
|
|
146
|
+
The Logo component currently does not require field configuration. All logo data comes from the HubSpot brand settings via the `useBrandSettings()` hook, or can be passed down from the parent module. This eliminates the need for repetitive logo field configuration across modules and ensures brand consistency.
|
|
147
|
+
|
|
148
|
+
## Styling
|
|
149
|
+
|
|
150
|
+
### CSS Variables
|
|
151
|
+
|
|
152
|
+
The Logo component uses CSS variables for theming and customization:
|
|
153
|
+
|
|
154
|
+
**Layout:**
|
|
155
|
+
- `--hscl-logo-display`: Display property (default: `inline-block`)
|
|
156
|
+
- `--hscl-logo-maxWidth`: Maximum width constraint (set via `maxWidth` prop, defaults to `100%`). When only `maxHeight` is provided, this is automatically set to `fit-content` via the component logic to maintain aspect ratio
|
|
157
|
+
- `--hscl-logo-maxHeight`: Maximum height constraint (set via `maxHeight` prop, defaults to `none`)
|
|
158
|
+
|
|
159
|
+
**Interactive States:**
|
|
160
|
+
- `--hscl-logo-cursor-hover`: Cursor style on hover when logo is wrapped in a link (default: `pointer`). When the logo is not wrapped in a link, the default cursor is `default`.
|
|
161
|
+
- `--hscl-logo-outlineWidth-focus`: Focus outline width (default: `2px`)
|
|
162
|
+
- `--hscl-logo-outlineColor-focus`: Focus outline color (default: `currentColor`)
|
|
163
|
+
- `--hscl-logo-outlineOffset-focus`: Focus outline offset (default: `2px`)
|
|
164
|
+
|
|
165
|
+
## Accessibility
|
|
166
|
+
|
|
167
|
+
The Logo component follows accessibility best practices:
|
|
168
|
+
|
|
169
|
+
- **Alt Text**: Uses alt text from brand settings for screen readers. Always provide meaningful alt text in your brand settings (e.g., "Company Name Logo" rather than just "Logo")
|
|
170
|
+
- **Semantic Links**: When a link is configured in brand settings, the logo automatically wraps in a semantic `<Link>` component with proper accessibility attributes
|
|
171
|
+
- **Keyboard Navigation**: Focus states are clearly visible with customizable outline styles for keyboard users
|
|
172
|
+
- **Image Loading**: The `loading` prop allows control over image loading behavior, preventing layout shifts that can confuse assistive technology users
|
|
173
|
+
- **Responsive Images**: Uses the Image component internally to provide responsive image handling for better performance across devices
|
|
174
|
+
|
|
175
|
+
## Best Practices
|
|
176
|
+
|
|
177
|
+
### Brand Settings Configuration
|
|
178
|
+
- **Data source**: Logo data comes from HubSpot brand settings via the `useBrandSettings()` hook. If logo data is not showing up correctly, check the brand settings configuration first
|
|
179
|
+
- **Alt text**: The component uses alt text from brand settings. If accessibility issues arise, verify that meaningful alt text (e.g., "Acme Corporation Logo") is configured in brand settings rather than generic text
|
|
180
|
+
- **Logo linking**: The component automatically wraps the logo in a link if a link URL is configured in brand settings. If linking behavior is unexpected, check the brand settings link configuration
|
|
181
|
+
- **Image quality**: Logo display quality depends on the image uploaded to brand settings. For debugging display issues, verify that appropriately sized images (recommended 2x resolution for retina displays) are configured
|
|
182
|
+
|
|
183
|
+
### Size Constraints
|
|
184
|
+
- **Use maxWidth in constrained layouts**: Navigation bars and footers often need size limits to maintain layout integrity
|
|
185
|
+
- **Use maxHeight for height-only constraints**: When you only need to constrain height, the component automatically handles width sizing to maintain aspect ratio
|
|
186
|
+
- **Maintain aspect ratio**: The component preserves the logo's aspect ratio from brand settings. Use `maxWidth` and `maxHeight` together to constrain both dimensions, or use one alone to constrain a single dimension
|
|
187
|
+
- **Consider responsive breakpoints**: Use CSS variables or style prop with media queries for responsive sizing
|
|
188
|
+
|
|
189
|
+
### Performance
|
|
190
|
+
- **Use eager loading for above-the-fold logos**: Navigation logos should load immediately (`loading="eager"`)
|
|
191
|
+
- **Use lazy loading for below-the-fold logos**: Footer or secondary logos can use `loading="lazy"` to improve initial page load
|
|
192
|
+
- **Optimize source images**: Ensure logos in brand settings are appropriately sized and compressed
|
|
193
|
+
|
|
194
|
+
### Development
|
|
195
|
+
- **Use useDummyLogo only in development**: This prop is for testing and should not be used in production code
|
|
196
|
+
- **Test with real brand settings**: Always test your modules with actual brand settings before deployment
|
|
197
|
+
|
|
198
|
+
### Layout Integration
|
|
199
|
+
- **Combine with navigation components**: Logos commonly appear alongside navigation menus in headers
|
|
200
|
+
- **Provide adequate spacing**: Use CSS margins or padding to ensure the logo has breathing room
|
|
201
|
+
- **Consider dark/light themes**: If your site has theme variations, test logo visibility in both contexts
|
|
202
|
+
|
|
203
|
+
## Integration with Other Components
|
|
204
|
+
|
|
205
|
+
The Logo component internally uses:
|
|
206
|
+
|
|
207
|
+
- **Image Component**: For responsive image rendering with proper attributes
|
|
208
|
+
- **Link Component**: For accessible link wrapping when logo.link is configured
|
|
209
|
+
|
|
210
|
+
This ensures consistency with other components in the library and leverages shared functionality for images and navigation.
|
|
211
|
+
|
|
212
|
+
## Performance Considerations
|
|
213
|
+
|
|
214
|
+
### Loading Behavior
|
|
215
|
+
- `loading="eager"`: Image loads immediately (default) - use for critical above-the-fold logos
|
|
216
|
+
- `loading="lazy"`: Image loads when near viewport - use for below-the-fold logos to improve initial page load
|
|
217
|
+
|
|
218
|
+
### Responsive Images
|
|
219
|
+
The component uses the Image component internally, which handles:
|
|
220
|
+
- Responsive image sizing based on container
|
|
221
|
+
- Proper aspect ratio maintenance
|
|
222
|
+
- Efficient loading strategies
|
|
223
|
+
|
|
224
|
+
## Common Use Cases
|
|
225
|
+
|
|
226
|
+
### Navigation Header
|
|
227
|
+
```tsx
|
|
228
|
+
<header style={{ display: 'flex', alignItems: 'center', padding: '1rem' }}>
|
|
229
|
+
<Logo maxHeight="50px" loading="eager" />
|
|
230
|
+
<nav>{/* Navigation menu */}</nav>
|
|
231
|
+
</header>
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Footer Branding
|
|
235
|
+
```tsx
|
|
236
|
+
<footer style={{ padding: '2rem', backgroundColor: '#f5f5f5' }}>
|
|
237
|
+
<Logo maxWidth="180px" loading="lazy" />
|
|
238
|
+
<p>© 2026 Company Name. All rights reserved.</p>
|
|
239
|
+
</footer>
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### Centered Hero Logo
|
|
243
|
+
```tsx
|
|
244
|
+
<section style={{ textAlign: 'center', padding: '4rem' }}>
|
|
245
|
+
<Logo
|
|
246
|
+
maxWidth="300px"
|
|
247
|
+
style={{ '--hscl-logo-display': 'block', margin: '0 auto' }}
|
|
248
|
+
/>
|
|
249
|
+
<h1>Welcome to Our Site</h1>
|
|
250
|
+
</section>
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### Sidebar Logo
|
|
254
|
+
```tsx
|
|
255
|
+
<aside>
|
|
256
|
+
<Logo
|
|
257
|
+
maxWidth="100px"
|
|
258
|
+
style={{ '--hscl-logo-display': 'block' }}
|
|
259
|
+
/>
|
|
260
|
+
{/* Sidebar content */}
|
|
261
|
+
</aside>
|
|
262
|
+
```
|