@hubspot/cms-component-library 0.1.0 → 0.2.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 +5 -3
- package/components/componentLibrary/Button/index.module.scss +22 -14
- package/components/componentLibrary/Button/index.tsx +6 -6
- 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/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 +4 -2
- /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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Decorator } from '@storybook/react';
|
|
2
|
-
import styles from './GridDecorator.module.
|
|
2
|
+
import styles from './GridDecorator.module.scss';
|
|
3
3
|
|
|
4
|
-
export const withGridStyles: Decorator =
|
|
4
|
+
export const withGridStyles: Decorator = Story => {
|
|
5
5
|
return (
|
|
6
6
|
<div className={styles.storyWrapper}>
|
|
7
7
|
<Story />
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { ChoiceField, TextField } from '@hubspot/cms-components/fields';
|
|
2
2
|
import type { ContentFieldsProps } from './types.js';
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
const ContentFields = ({
|
|
5
5
|
headingTextLabel = 'Heading text',
|
|
6
6
|
headingTextName = 'headingText',
|
|
7
7
|
headingTextDefault = 'Heading Text',
|
|
8
8
|
headingLevelLabel = 'Heading level',
|
|
9
9
|
headingLevelName = 'headingLevel',
|
|
10
10
|
headingLevelDefault = 'h1',
|
|
11
|
-
}: ContentFieldsProps) {
|
|
11
|
+
}: ContentFieldsProps) => {
|
|
12
12
|
return (
|
|
13
13
|
<>
|
|
14
14
|
<TextField
|
|
@@ -32,4 +32,6 @@ export default function ContentFields({
|
|
|
32
32
|
/>
|
|
33
33
|
</>
|
|
34
34
|
);
|
|
35
|
-
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export default ContentFields;
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import { AlignmentField, ChoiceField } from '@hubspot/cms-components/fields';
|
|
2
2
|
import type { StyleFieldsProps } from './types.js';
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
const StyleFields = ({
|
|
5
|
+
textAlignLabel = 'Heading alignment',
|
|
6
|
+
textAlignName = 'headingTextAlign',
|
|
7
|
+
textAlignDefault = 'LEFT',
|
|
8
8
|
displayAsLabel = 'Display as',
|
|
9
9
|
displayAsName = 'headingDisplayAs',
|
|
10
10
|
displayAsDefault = 'h1',
|
|
11
|
-
}: StyleFieldsProps) {
|
|
11
|
+
}: StyleFieldsProps) => {
|
|
12
12
|
return (
|
|
13
13
|
<>
|
|
14
14
|
<AlignmentField
|
|
15
|
-
label={
|
|
16
|
-
name={
|
|
15
|
+
label={textAlignLabel}
|
|
16
|
+
name={textAlignName}
|
|
17
17
|
alignmentDirection="HORIZONTAL"
|
|
18
|
-
default={{ horizontal_align:
|
|
18
|
+
default={{ horizontal_align: textAlignDefault }}
|
|
19
19
|
/>
|
|
20
20
|
<ChoiceField
|
|
21
21
|
label={displayAsLabel}
|
|
@@ -35,4 +35,6 @@ export default function StyleFields({
|
|
|
35
35
|
/>
|
|
36
36
|
</>
|
|
37
37
|
);
|
|
38
|
-
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export default StyleFields;
|
|
@@ -8,7 +8,7 @@ import { HeadingProps } from './types.js';
|
|
|
8
8
|
const HeadingComponent = ({
|
|
9
9
|
headingLevel,
|
|
10
10
|
displayAs,
|
|
11
|
-
|
|
11
|
+
textAlign,
|
|
12
12
|
className = '',
|
|
13
13
|
style = {},
|
|
14
14
|
children = 'A clear and bold heading',
|
|
@@ -22,8 +22,8 @@ const HeadingComponent = ({
|
|
|
22
22
|
'--hscl-heading-fontSize': `var(--hscl-heading-${displayAsValue}-fontSize)`,
|
|
23
23
|
'--hscl-heading-fontStyle': `var(--hscl-heading-${displayAsValue}-fontStyle)`,
|
|
24
24
|
'--hscl-heading-fontWeight': `var(--hscl-heading-${displayAsValue}-fontWeight)`,
|
|
25
|
-
...(
|
|
26
|
-
'--hscl-heading-textAlign':
|
|
25
|
+
...(textAlign && {
|
|
26
|
+
'--hscl-heading-textAlign': textAlign.toLowerCase(),
|
|
27
27
|
}),
|
|
28
28
|
};
|
|
29
29
|
|
|
@@ -32,7 +32,7 @@ Heading/
|
|
|
32
32
|
{
|
|
33
33
|
headingLevel: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'; // Semantic HTML level (required)
|
|
34
34
|
displayAs?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'display_1' | 'display_2'; // Visual style (defaults to headingLevel)
|
|
35
|
-
|
|
35
|
+
textAlign?: 'LEFT' | 'CENTER' | 'RIGHT'; // Text alignment
|
|
36
36
|
className?: string; // Additional CSS classes
|
|
37
37
|
style?: React.CSSProperties; // Inline styles (including CSS variables)
|
|
38
38
|
children?: React.ReactNode; // Heading text content
|
|
@@ -64,12 +64,12 @@ When you need a lower-level heading to look like a higher-level one:
|
|
|
64
64
|
</Heading>
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
-
### With Alignment
|
|
67
|
+
### With Text Alignment
|
|
68
68
|
|
|
69
69
|
```tsx
|
|
70
70
|
<Heading
|
|
71
71
|
headingLevel="h2"
|
|
72
|
-
|
|
72
|
+
textAlign="CENTER"
|
|
73
73
|
>
|
|
74
74
|
Centered Heading
|
|
75
75
|
</Heading>
|
|
@@ -101,9 +101,9 @@ When you need a lower-level heading to look like a higher-level one:
|
|
|
101
101
|
|
|
102
102
|
```tsx
|
|
103
103
|
<Heading.StyleFields
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
104
|
+
textAlignLabel="Heading text align"
|
|
105
|
+
textAlignName="headingTextAlign"
|
|
106
|
+
textAlignDefault="LEFT"
|
|
107
107
|
displayAsLabel="Display as"
|
|
108
108
|
displayAsName="headingDisplayAs"
|
|
109
109
|
displayAsDefault="h1"
|
|
@@ -111,7 +111,7 @@ When you need a lower-level heading to look like a higher-level one:
|
|
|
111
111
|
```
|
|
112
112
|
|
|
113
113
|
**Fields:**
|
|
114
|
-
- `
|
|
114
|
+
- `headingTextAlign`: AlignmentField for text alignment
|
|
115
115
|
- `headingDisplayAs`: ChoiceField for visual style (h1-h6, display_1, display_2)
|
|
116
116
|
|
|
117
117
|
### Module Usage Example
|
|
@@ -124,7 +124,7 @@ export default function HeroModule({ fieldValues }) {
|
|
|
124
124
|
<Heading
|
|
125
125
|
headingLevel={fieldValues.headingLevel}
|
|
126
126
|
displayAs={fieldValues.headingDisplayAs}
|
|
127
|
-
|
|
127
|
+
textAlign={fieldValues.headingTextAlign?.horizontal_align}
|
|
128
128
|
>
|
|
129
129
|
{fieldValues.headingText}
|
|
130
130
|
</Heading>
|
|
@@ -52,17 +52,17 @@ export const TextAlignment: Story = {
|
|
|
52
52
|
render: () => (
|
|
53
53
|
<SBContainer flex direction="column" gap="large" minWidth="500px">
|
|
54
54
|
<SBContainer addBackground>
|
|
55
|
-
<Heading headingLevel="h2"
|
|
55
|
+
<Heading headingLevel="h2" textAlign="LEFT">
|
|
56
56
|
Left Aligned
|
|
57
57
|
</Heading>
|
|
58
58
|
</SBContainer>
|
|
59
59
|
<SBContainer addBackground>
|
|
60
|
-
<Heading headingLevel="h2"
|
|
60
|
+
<Heading headingLevel="h2" textAlign="CENTER">
|
|
61
61
|
Center Aligned
|
|
62
62
|
</Heading>
|
|
63
63
|
</SBContainer>
|
|
64
64
|
<SBContainer addBackground>
|
|
65
|
-
<Heading headingLevel="h2"
|
|
65
|
+
<Heading headingLevel="h2" textAlign="RIGHT">
|
|
66
66
|
Right Aligned
|
|
67
67
|
</Heading>
|
|
68
68
|
</SBContainer>
|
|
@@ -10,7 +10,7 @@ export type DisplayAs = HeadingLevel | 'display_1' | 'display_2';
|
|
|
10
10
|
export type HeadingProps = {
|
|
11
11
|
headingLevel: HeadingLevel;
|
|
12
12
|
displayAs?: DisplayAs;
|
|
13
|
-
|
|
13
|
+
textAlign?: (typeof AlignmentFieldDefaults)['horizontal_align'];
|
|
14
14
|
className?: string;
|
|
15
15
|
style?: React.CSSProperties;
|
|
16
16
|
children?: React.ReactNode;
|
|
@@ -26,9 +26,9 @@ export type ContentFieldsProps = {
|
|
|
26
26
|
};
|
|
27
27
|
|
|
28
28
|
export type StyleFieldsProps = {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
textAlignLabel?: string;
|
|
30
|
+
textAlignName?: string;
|
|
31
|
+
textAlignDefault?: (typeof AlignmentFieldDefaults)['horizontal_align'];
|
|
32
32
|
displayAsLabel?: string;
|
|
33
33
|
displayAsName?: string;
|
|
34
34
|
displayAsDefault?: DisplayAs;
|
|
@@ -5,13 +5,13 @@ import {
|
|
|
5
5
|
} from '@hubspot/cms-components/fields';
|
|
6
6
|
import { ContentFieldsProps } from './types.js';
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
const ContentFields = ({
|
|
9
9
|
iconLabel = 'Icon',
|
|
10
10
|
iconName = 'icon',
|
|
11
11
|
iconDefault = { name: 'angle-right', unicode: 'f105', type: 'SOLID' },
|
|
12
12
|
addIconToggle = false,
|
|
13
13
|
...rest
|
|
14
|
-
}: ContentFieldsProps) {
|
|
14
|
+
}: ContentFieldsProps) => {
|
|
15
15
|
let showIconLabel = 'Show icon';
|
|
16
16
|
let showIconName = 'showIcon';
|
|
17
17
|
let showIconDefault = false;
|
|
@@ -48,4 +48,6 @@ export default function ContentFields({
|
|
|
48
48
|
/>
|
|
49
49
|
</>
|
|
50
50
|
);
|
|
51
|
-
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export default ContentFields;
|
|
@@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react';
|
|
|
2
2
|
import Icon from '../index.js';
|
|
3
3
|
import { IconProps } from '../types.js';
|
|
4
4
|
import { withIconStyles } from './IconDecorator.js';
|
|
5
|
-
import styles from './IconDecorator.module.
|
|
5
|
+
import styles from './IconDecorator.module.scss';
|
|
6
6
|
import { SBContainer } from '@sb-utils/SBContainer.js';
|
|
7
7
|
|
|
8
8
|
const meta: Meta<IconProps> = {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ImageField } from '@hubspot/cms-components/fields';
|
|
2
2
|
import { ContentFieldsProps } from './types.js';
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
const ContentFields = ({
|
|
5
5
|
imageLabel = 'Image',
|
|
6
6
|
imageName = 'image',
|
|
7
7
|
imageDefault = {
|
|
@@ -14,7 +14,7 @@ export default function ContentFields({
|
|
|
14
14
|
showLoading = true,
|
|
15
15
|
resizable = true,
|
|
16
16
|
responsive = true,
|
|
17
|
-
}: ContentFieldsProps) {
|
|
17
|
+
}: ContentFieldsProps) => {
|
|
18
18
|
return (
|
|
19
19
|
<>
|
|
20
20
|
<ImageField
|
|
@@ -27,4 +27,6 @@ export default function ContentFields({
|
|
|
27
27
|
/>
|
|
28
28
|
</>
|
|
29
29
|
);
|
|
30
|
-
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export default ContentFields;
|
|
@@ -7,8 +7,8 @@ const ImageComponent = ({
|
|
|
7
7
|
src,
|
|
8
8
|
alt,
|
|
9
9
|
title = '',
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
width,
|
|
11
|
+
height,
|
|
12
12
|
loading = 'lazy',
|
|
13
13
|
responsive = true,
|
|
14
14
|
className = '',
|
|
@@ -27,8 +27,8 @@ const ImageComponent = ({
|
|
|
27
27
|
src={src}
|
|
28
28
|
alt={alt}
|
|
29
29
|
title={title}
|
|
30
|
-
width={
|
|
31
|
-
height={
|
|
30
|
+
width={width}
|
|
31
|
+
height={height}
|
|
32
32
|
loading={loading !== 'disabled' ? loading : 'eager'}
|
|
33
33
|
className={combinedClasses}
|
|
34
34
|
style={style}
|
|
@@ -32,9 +32,9 @@ Image/
|
|
|
32
32
|
src: string; // Image source URL (required)
|
|
33
33
|
alt: string; // Alternative text for accessibility (required)
|
|
34
34
|
title?: string; // Tooltip text on hover
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
loading?: 'lazy' | 'eager' | 'disabled';
|
|
35
|
+
width?: number; // Image width in pixels
|
|
36
|
+
height?: number; // Image height in pixels
|
|
37
|
+
loading?: 'lazy' | 'eager' | 'disabled'; // Loading strategy (default: 'lazy')
|
|
38
38
|
responsive?: boolean; // Enable responsive scaling (default: true)
|
|
39
39
|
className?: string; // Additional CSS classes
|
|
40
40
|
style?: React.CSSProperties; // Inline styles (including CSS variables)
|
|
@@ -52,8 +52,8 @@ import Image from '@hubspot/cms-component-library/Image';
|
|
|
52
52
|
<Image
|
|
53
53
|
src="https://example.com/image.jpg"
|
|
54
54
|
alt="Description of the image"
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
width={800}
|
|
56
|
+
height={600}
|
|
57
57
|
/>
|
|
58
58
|
```
|
|
59
59
|
|
|
@@ -63,8 +63,8 @@ import Image from '@hubspot/cms-component-library/Image';
|
|
|
63
63
|
<Image
|
|
64
64
|
src="https://example.com/hero-image.jpg"
|
|
65
65
|
alt="Hero banner showcasing product features"
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
width={1200}
|
|
67
|
+
height={600}
|
|
68
68
|
responsive={true}
|
|
69
69
|
/>
|
|
70
70
|
```
|
|
@@ -76,8 +76,8 @@ import Image from '@hubspot/cms-component-library/Image';
|
|
|
76
76
|
src="https://example.com/product.jpg"
|
|
77
77
|
alt="Product showcase"
|
|
78
78
|
title="Click to view product details"
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
width={600}
|
|
80
|
+
height={400}
|
|
81
81
|
/>
|
|
82
82
|
```
|
|
83
83
|
|
|
@@ -87,8 +87,8 @@ import Image from '@hubspot/cms-component-library/Image';
|
|
|
87
87
|
<Image
|
|
88
88
|
src="https://example.com/hero.jpg"
|
|
89
89
|
alt="Hero image"
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
width={1920}
|
|
91
|
+
height={1080}
|
|
92
92
|
loading="eager"
|
|
93
93
|
responsive={true}
|
|
94
94
|
/>
|
|
@@ -100,8 +100,8 @@ import Image from '@hubspot/cms-component-library/Image';
|
|
|
100
100
|
<Image
|
|
101
101
|
src="https://example.com/profile.jpg"
|
|
102
102
|
alt="User profile picture"
|
|
103
|
-
|
|
104
|
-
|
|
103
|
+
width={200}
|
|
104
|
+
height={200}
|
|
105
105
|
style={{
|
|
106
106
|
borderRadius: '50%',
|
|
107
107
|
border: '4px solid #0066cc',
|
|
@@ -156,8 +156,8 @@ export default function HeroModule({ fieldValues }) {
|
|
|
156
156
|
<Image
|
|
157
157
|
src={fieldValues.heroImage?.src}
|
|
158
158
|
alt={fieldValues.heroImage?.alt}
|
|
159
|
-
|
|
160
|
-
|
|
159
|
+
width={fieldValues.heroImage?.width}
|
|
160
|
+
height={fieldValues.heroImage?.height}
|
|
161
161
|
loading={fieldValues.heroImage?.loading || 'lazy'}
|
|
162
162
|
responsive={true}
|
|
163
163
|
/>
|
|
@@ -194,14 +194,14 @@ The Image component follows accessibility best practices:
|
|
|
194
194
|
- For decorative images, use an empty string: `alt=""`
|
|
195
195
|
- **Semantic HTML**: Renders native `<img>` elements with proper attributes
|
|
196
196
|
- **Title Attribute**: Optional `title` prop provides additional context on hover
|
|
197
|
-
- **Dimensions**: `
|
|
197
|
+
- **Dimensions**: `width` and `height` props help browsers allocate space and prevent layout shift
|
|
198
198
|
|
|
199
199
|
## Best Practices
|
|
200
200
|
|
|
201
201
|
- **Always provide meaningful alt text**: Describe the content and purpose of the image for screen readers
|
|
202
202
|
- **Use lazy loading for below-the-fold images**: Keep the default `loading="lazy"` for images not immediately visible
|
|
203
203
|
- **Use eager loading for above-the-fold images**: Set `loading="eager"` for hero images and critical content
|
|
204
|
-
- **Specify dimensions**: Always provide `
|
|
204
|
+
- **Specify dimensions**: Always provide `width` and `height` to prevent cumulative layout shift (CLS)
|
|
205
205
|
- **Enable responsive behavior**: Keep `responsive={true}` (default) unless you need fixed dimensions
|
|
206
206
|
- **CSS Variables for consistent styling**: Use CSS variables rather than inline styles for site-wide theming
|
|
207
207
|
- **Style fields**: The image component doesn't include style fields. Do not try to import them.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
2
|
import Image from '../index.js';
|
|
3
3
|
import { withImageStyles } from './ImageDecorator.js';
|
|
4
|
-
import { SBContainer } from '@sb-utils
|
|
5
|
-
import placeholderImage from './assets/
|
|
4
|
+
import { SBContainer, SBFocusWrapper } from '@sb-utils';
|
|
5
|
+
import placeholderImage from './assets/cat-smile.jpg';
|
|
6
6
|
|
|
7
7
|
const meta: Meta<typeof Image> = {
|
|
8
8
|
title: 'Component Library/Image',
|
|
@@ -26,8 +26,8 @@ export const Default: Story = {
|
|
|
26
26
|
args: {
|
|
27
27
|
src: placeholderImage,
|
|
28
28
|
alt: 'A placeholder image demonstrating default Image component behavior',
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
width: 600,
|
|
30
|
+
height: 400,
|
|
31
31
|
loading: 'lazy',
|
|
32
32
|
responsive: true,
|
|
33
33
|
},
|
|
@@ -47,8 +47,8 @@ export const ResponsiveBehavior: Story = {
|
|
|
47
47
|
<Image
|
|
48
48
|
src={placeholderImage}
|
|
49
49
|
alt="Responsive image"
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
width={800}
|
|
51
|
+
height={600}
|
|
52
52
|
responsive={true}
|
|
53
53
|
/>
|
|
54
54
|
</SBContainer>
|
|
@@ -67,8 +67,8 @@ export const ResponsiveBehavior: Story = {
|
|
|
67
67
|
<Image
|
|
68
68
|
src={placeholderImage}
|
|
69
69
|
alt="Non-responsive image at full 800x600 size"
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
width={800}
|
|
71
|
+
height={600}
|
|
72
72
|
responsive={false}
|
|
73
73
|
style={{ width: '800px', height: '600px' }}
|
|
74
74
|
/>
|
|
@@ -92,8 +92,8 @@ export const AccessibilityAltText: Story = {
|
|
|
92
92
|
src={placeholderImage}
|
|
93
93
|
alt="Sample image with tooltip"
|
|
94
94
|
title="Hover over me to see the title tooltip"
|
|
95
|
-
|
|
96
|
-
|
|
95
|
+
width={400}
|
|
96
|
+
height={300}
|
|
97
97
|
responsive={true}
|
|
98
98
|
/>
|
|
99
99
|
<p style={{ marginTop: '8px', fontSize: '14px', color: '#666' }}>
|
|
@@ -114,8 +114,8 @@ export const EdgeCases: Story = {
|
|
|
114
114
|
<Image
|
|
115
115
|
src="fake-doesn't-exist.jpg"
|
|
116
116
|
alt="This image failed to load - alt text is displayed"
|
|
117
|
-
|
|
118
|
-
|
|
117
|
+
width={400}
|
|
118
|
+
height={300}
|
|
119
119
|
responsive={true}
|
|
120
120
|
/>
|
|
121
121
|
</SBContainer>
|
|
@@ -132,8 +132,8 @@ export const WithCustomStyling: Story = {
|
|
|
132
132
|
<Image
|
|
133
133
|
src={placeholderImage}
|
|
134
134
|
alt="Image with rounded corners"
|
|
135
|
-
|
|
136
|
-
|
|
135
|
+
width={400}
|
|
136
|
+
height={300}
|
|
137
137
|
responsive={true}
|
|
138
138
|
style={{ borderRadius: '16px' }}
|
|
139
139
|
/>
|
|
@@ -144,8 +144,8 @@ export const WithCustomStyling: Story = {
|
|
|
144
144
|
<Image
|
|
145
145
|
src={placeholderImage}
|
|
146
146
|
alt="Image with border"
|
|
147
|
-
|
|
148
|
-
|
|
147
|
+
width={400}
|
|
148
|
+
height={300}
|
|
149
149
|
responsive={true}
|
|
150
150
|
style={{ border: '4px solid #3498db', borderRadius: '8px' }}
|
|
151
151
|
/>
|
|
@@ -156,8 +156,8 @@ export const WithCustomStyling: Story = {
|
|
|
156
156
|
<Image
|
|
157
157
|
src={placeholderImage}
|
|
158
158
|
alt="Image with drop shadow"
|
|
159
|
-
|
|
160
|
-
|
|
159
|
+
width={400}
|
|
160
|
+
height={300}
|
|
161
161
|
responsive={true}
|
|
162
162
|
style={{
|
|
163
163
|
boxShadow: '0 10px 30px rgba(0,0,0,0.3)',
|
|
@@ -168,3 +168,46 @@ export const WithCustomStyling: Story = {
|
|
|
168
168
|
</SBContainer>
|
|
169
169
|
),
|
|
170
170
|
};
|
|
171
|
+
|
|
172
|
+
export const InteractionStates: Story = {
|
|
173
|
+
name: 'Interaction States (Wrapped in Link)',
|
|
174
|
+
render: () => (
|
|
175
|
+
<SBContainer flex direction="column" gap="large">
|
|
176
|
+
<SBContainer addBackground>
|
|
177
|
+
<h4>Clickable Image</h4>
|
|
178
|
+
<p>Image wrapped in a link becomes clickable and shows hover effects</p>
|
|
179
|
+
<a href="https://www.hubspot.com" style={{ textDecoration: 'none' }}>
|
|
180
|
+
<Image
|
|
181
|
+
src={placeholderImage}
|
|
182
|
+
alt="Clickable image"
|
|
183
|
+
width={400}
|
|
184
|
+
height={300}
|
|
185
|
+
responsive={true}
|
|
186
|
+
style={{ borderRadius: '8px' }}
|
|
187
|
+
/>
|
|
188
|
+
</a>
|
|
189
|
+
</SBContainer>
|
|
190
|
+
|
|
191
|
+
<SBContainer addBackground>
|
|
192
|
+
<h4>Focus State</h4>
|
|
193
|
+
<p>
|
|
194
|
+
In production, this focus outline appears when navigating with the
|
|
195
|
+
keyboard (Tab key). This Storybook demo auto-applies the focus state
|
|
196
|
+
to show how it looks.
|
|
197
|
+
</p>
|
|
198
|
+
<SBFocusWrapper>
|
|
199
|
+
<a href="https://www.hubspot.com" style={{ textDecoration: 'none' }}>
|
|
200
|
+
<Image
|
|
201
|
+
src={placeholderImage}
|
|
202
|
+
alt="Focused image wrapped in link"
|
|
203
|
+
width={400}
|
|
204
|
+
height={300}
|
|
205
|
+
responsive={true}
|
|
206
|
+
style={{ borderRadius: '8px' }}
|
|
207
|
+
/>
|
|
208
|
+
</a>
|
|
209
|
+
</SBFocusWrapper>
|
|
210
|
+
</SBContainer>
|
|
211
|
+
</SBContainer>
|
|
212
|
+
),
|
|
213
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { TextField } from '@hubspot/cms-components/fields';
|
|
2
|
+
import type { ContentFieldsProps } from './types.js';
|
|
3
|
+
|
|
4
|
+
const ContentFields = ({
|
|
5
|
+
languageSwitcherSelectTextLabel = 'Language selector text',
|
|
6
|
+
languageSwitcherSelectTextName = 'languageSwitcherSelectText',
|
|
7
|
+
languageSwitcherSelectTextDefault = 'Select a language',
|
|
8
|
+
}: ContentFieldsProps) => {
|
|
9
|
+
return (
|
|
10
|
+
<TextField
|
|
11
|
+
label={languageSwitcherSelectTextLabel}
|
|
12
|
+
name={languageSwitcherSelectTextName}
|
|
13
|
+
default={languageSwitcherSelectTextDefault}
|
|
14
|
+
/>
|
|
15
|
+
);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export default ContentFields;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
.languageList {
|
|
2
|
+
background-color: var(--hscl-languageSwitcher-backgroundColor, transparent);
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
.languageItem {
|
|
6
|
+
&:last-child {
|
|
7
|
+
margin-bottom: 0;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.languageItem--active {
|
|
12
|
+
background-color: var(--hscl-languageSwitcher-backgroundColor-hover, transparent);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.languageItem .languageLink {
|
|
16
|
+
display: flex;
|
|
17
|
+
padding: 12px 16px;
|
|
18
|
+
align-items: center;
|
|
19
|
+
color: var(--hscl-languageSwitcher-color, #000000);
|
|
20
|
+
font-size: var(--hscl-languageSwitcher-fontSize, 18px);
|
|
21
|
+
font-weight: 600;
|
|
22
|
+
gap: 12px;
|
|
23
|
+
text-decoration: none;
|
|
24
|
+
transition: background-color 0.2s ease;
|
|
25
|
+
|
|
26
|
+
&:focus,
|
|
27
|
+
&:active,
|
|
28
|
+
&:visited,
|
|
29
|
+
&:hover {
|
|
30
|
+
color: var(--hscl-languageSwitcher-color-hover, #000000);
|
|
31
|
+
text-decoration: none;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
&:hover {
|
|
35
|
+
background-color: var(--hscl-languageSwitcher-backgroundColor-hover, #f1f5f9);
|
|
36
|
+
}
|
|
37
|
+
}
|