@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
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import LanguageSwitcher from '../index.js';
|
|
3
|
+
import { LanguageSwitcherProps } from '../types.js';
|
|
4
|
+
import { withLanguageSwitcherStyles } from './LanguageSwitcherDecorator.js';
|
|
5
|
+
import { SBContainer, SBFocusWrapper } from '@sb-utils';
|
|
6
|
+
|
|
7
|
+
const meta: Meta<LanguageSwitcherProps> = {
|
|
8
|
+
title: 'Component Library/LanguageSwitcher',
|
|
9
|
+
component: LanguageSwitcher,
|
|
10
|
+
parameters: {
|
|
11
|
+
layout: 'fullscreen',
|
|
12
|
+
docs: {
|
|
13
|
+
description: {
|
|
14
|
+
component: `The LanguageSwitcher component provides a button and drawer interface for selecting different language variants of a page. It displays the current page language and opens a slide-out panel with all available language options. The component uses the Drawer component internally for the language selection panel.`,
|
|
15
|
+
},
|
|
16
|
+
story: {
|
|
17
|
+
inline: false,
|
|
18
|
+
iframeHeight: 500,
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
decorators: [withLanguageSwitcherStyles],
|
|
23
|
+
argTypes: {
|
|
24
|
+
useDummyData: {
|
|
25
|
+
control: 'boolean',
|
|
26
|
+
description: 'Use dummy translation data for testing',
|
|
27
|
+
table: {
|
|
28
|
+
category: 'Development',
|
|
29
|
+
defaultValue: { summary: 'false' },
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
languageSwitcherSelectText: {
|
|
33
|
+
control: 'text',
|
|
34
|
+
description: 'Text displayed in the drawer header',
|
|
35
|
+
table: {
|
|
36
|
+
category: 'Content',
|
|
37
|
+
defaultValue: { summary: 'Select a language' },
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
textColor: {
|
|
41
|
+
control: 'object',
|
|
42
|
+
description: 'Text color for button and menu items',
|
|
43
|
+
table: {
|
|
44
|
+
category: 'Visual',
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
textColorHover: {
|
|
48
|
+
control: 'object',
|
|
49
|
+
description: 'Text color for menu items on hover',
|
|
50
|
+
table: {
|
|
51
|
+
category: 'Visual',
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
menuBackgroundColor: {
|
|
55
|
+
control: 'object',
|
|
56
|
+
description: 'Background color for the drawer',
|
|
57
|
+
table: {
|
|
58
|
+
category: 'Visual',
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
menuBackgroundColorHover: {
|
|
62
|
+
control: 'object',
|
|
63
|
+
description: 'Background color for menu items on hover',
|
|
64
|
+
table: {
|
|
65
|
+
category: 'Visual',
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
className: {
|
|
69
|
+
control: 'text',
|
|
70
|
+
description: 'Additional CSS classes',
|
|
71
|
+
table: {
|
|
72
|
+
category: 'Basic',
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
style: {
|
|
76
|
+
control: 'object',
|
|
77
|
+
description: 'Inline styles',
|
|
78
|
+
table: {
|
|
79
|
+
category: 'Basic',
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export default meta;
|
|
86
|
+
type Story = StoryObj<typeof meta>;
|
|
87
|
+
|
|
88
|
+
export const Default: Story = {
|
|
89
|
+
render: () => {
|
|
90
|
+
return (
|
|
91
|
+
<SBContainer>
|
|
92
|
+
<LanguageSwitcher
|
|
93
|
+
useDummyData={true}
|
|
94
|
+
languageSwitcherSelectText="Select a language"
|
|
95
|
+
/>
|
|
96
|
+
</SBContainer>
|
|
97
|
+
);
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export const ColorThemes: Story = {
|
|
102
|
+
name: 'Color Themes',
|
|
103
|
+
render: () => {
|
|
104
|
+
const purpleTextColor = {
|
|
105
|
+
rgba: 'rgba(139, 0, 139, 1)',
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const purpleTextColorHover = {
|
|
109
|
+
rgba: 'rgba(186, 85, 211, 1)',
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const purpleMenuBgColor = {
|
|
113
|
+
rgba: 'rgba(243, 229, 245, 1)',
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const purpleMenuBgColorHover = {
|
|
117
|
+
rgba: 'rgba(225, 190, 231, 1)',
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const darkTextColor = {
|
|
121
|
+
rgba: 'rgba(255, 255, 255, 0.9)',
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const darkTextColorHover = {
|
|
125
|
+
rgba: 'rgba(100, 181, 246, 1)',
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const darkMenuBgColor = {
|
|
129
|
+
rgba: 'rgba(33, 33, 33, 1)',
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const darkMenuBgColorHover = {
|
|
133
|
+
rgba: 'rgba(66, 66, 66, 1)',
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
return (
|
|
137
|
+
<SBContainer flex direction="column" gap="large">
|
|
138
|
+
<SBContainer addBackground>
|
|
139
|
+
<h4>Purple Theme</h4>
|
|
140
|
+
<p>Custom color theming with purple tones.</p>
|
|
141
|
+
<SBContainer>
|
|
142
|
+
<LanguageSwitcher
|
|
143
|
+
useDummyData={true}
|
|
144
|
+
languageSwitcherSelectText="Wählen Sie eine Sprache"
|
|
145
|
+
textColor={purpleTextColor}
|
|
146
|
+
textColorHover={purpleTextColorHover}
|
|
147
|
+
menuBackgroundColor={purpleMenuBgColor}
|
|
148
|
+
menuBackgroundColorHover={purpleMenuBgColorHover}
|
|
149
|
+
/>
|
|
150
|
+
</SBContainer>
|
|
151
|
+
</SBContainer>
|
|
152
|
+
|
|
153
|
+
<SBContainer style={{ backgroundColor: '#121212' }}>
|
|
154
|
+
<SBContainer style={{ marginBottom: '16px' }}>
|
|
155
|
+
<h4 style={{ color: 'white', margin: '0 0 8px 0' }}>
|
|
156
|
+
Dark Mode Theme
|
|
157
|
+
</h4>
|
|
158
|
+
<p style={{ color: 'white', margin: 0 }}>
|
|
159
|
+
Language switcher optimized for dark backgrounds with high
|
|
160
|
+
contrast colors.
|
|
161
|
+
</p>
|
|
162
|
+
</SBContainer>
|
|
163
|
+
<SBContainer>
|
|
164
|
+
<LanguageSwitcher
|
|
165
|
+
useDummyData={true}
|
|
166
|
+
languageSwitcherSelectText="Select a language"
|
|
167
|
+
textColor={darkTextColor}
|
|
168
|
+
textColorHover={darkTextColorHover}
|
|
169
|
+
menuBackgroundColor={darkMenuBgColor}
|
|
170
|
+
menuBackgroundColorHover={darkMenuBgColorHover}
|
|
171
|
+
/>
|
|
172
|
+
</SBContainer>
|
|
173
|
+
</SBContainer>
|
|
174
|
+
</SBContainer>
|
|
175
|
+
);
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
export const CustomText: Story = {
|
|
180
|
+
name: 'Custom Header Text',
|
|
181
|
+
render: () => {
|
|
182
|
+
return (
|
|
183
|
+
<SBContainer flex direction="column" gap="large">
|
|
184
|
+
<SBContainer addBackground>
|
|
185
|
+
<h4>Different Languages for Header Text</h4>
|
|
186
|
+
<p>
|
|
187
|
+
The languageSwitcherSelectText prop can be customized to match your
|
|
188
|
+
site's language or messaging.
|
|
189
|
+
</p>
|
|
190
|
+
</SBContainer>
|
|
191
|
+
|
|
192
|
+
<SBContainer flex direction="row" gap="medium">
|
|
193
|
+
<SBContainer>
|
|
194
|
+
<p style={{ marginBottom: '12px' }}>
|
|
195
|
+
<strong>🇬🇧 English:</strong>
|
|
196
|
+
</p>
|
|
197
|
+
<LanguageSwitcher
|
|
198
|
+
useDummyData={true}
|
|
199
|
+
languageSwitcherSelectText="Select a language"
|
|
200
|
+
/>
|
|
201
|
+
</SBContainer>
|
|
202
|
+
|
|
203
|
+
<SBContainer>
|
|
204
|
+
<p style={{ marginBottom: '12px' }}>
|
|
205
|
+
<strong>🇩🇪 German:</strong>
|
|
206
|
+
</p>
|
|
207
|
+
<LanguageSwitcher
|
|
208
|
+
useDummyData={true}
|
|
209
|
+
languageSwitcherSelectText="Sprache wählen"
|
|
210
|
+
/>
|
|
211
|
+
</SBContainer>
|
|
212
|
+
|
|
213
|
+
<SBContainer>
|
|
214
|
+
<p style={{ marginBottom: '12px' }}>
|
|
215
|
+
<strong>🇪🇸 Spanish:</strong>
|
|
216
|
+
</p>
|
|
217
|
+
<LanguageSwitcher
|
|
218
|
+
useDummyData={true}
|
|
219
|
+
languageSwitcherSelectText="Seleccionar idioma"
|
|
220
|
+
/>
|
|
221
|
+
</SBContainer>
|
|
222
|
+
|
|
223
|
+
<SBContainer>
|
|
224
|
+
<p style={{ marginBottom: '12px' }}>
|
|
225
|
+
<strong>🇫🇷 French:</strong>
|
|
226
|
+
</p>
|
|
227
|
+
<LanguageSwitcher
|
|
228
|
+
useDummyData={true}
|
|
229
|
+
languageSwitcherSelectText="Choisir une langue"
|
|
230
|
+
/>
|
|
231
|
+
</SBContainer>
|
|
232
|
+
</SBContainer>
|
|
233
|
+
</SBContainer>
|
|
234
|
+
);
|
|
235
|
+
},
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
export const InContext: Story = {
|
|
239
|
+
name: 'In Page Context',
|
|
240
|
+
render: () => {
|
|
241
|
+
return (
|
|
242
|
+
<SBContainer style={{ minHeight: '100vh' }}>
|
|
243
|
+
<header
|
|
244
|
+
style={{
|
|
245
|
+
backgroundColor: '#0066cc',
|
|
246
|
+
color: 'white',
|
|
247
|
+
padding: '16px 24px',
|
|
248
|
+
display: 'flex',
|
|
249
|
+
justifyContent: 'space-between',
|
|
250
|
+
alignItems: 'center',
|
|
251
|
+
}}
|
|
252
|
+
>
|
|
253
|
+
<SBContainer>
|
|
254
|
+
<h1 style={{ margin: 0, fontSize: '24px', color: 'white' }}>
|
|
255
|
+
My Website
|
|
256
|
+
</h1>
|
|
257
|
+
<p
|
|
258
|
+
style={{
|
|
259
|
+
margin: '4px 0 0 0',
|
|
260
|
+
fontSize: '14px',
|
|
261
|
+
opacity: 0.75,
|
|
262
|
+
color: 'white',
|
|
263
|
+
}}
|
|
264
|
+
>
|
|
265
|
+
Global multilingual site
|
|
266
|
+
</p>
|
|
267
|
+
</SBContainer>
|
|
268
|
+
|
|
269
|
+
<nav
|
|
270
|
+
style={{
|
|
271
|
+
display: 'flex',
|
|
272
|
+
gap: '24px',
|
|
273
|
+
alignItems: 'center',
|
|
274
|
+
}}
|
|
275
|
+
>
|
|
276
|
+
<a href="#home" style={{ color: 'white', textDecoration: 'none' }}>
|
|
277
|
+
Home
|
|
278
|
+
</a>
|
|
279
|
+
<a href="#about" style={{ color: 'white', textDecoration: 'none' }}>
|
|
280
|
+
About
|
|
281
|
+
</a>
|
|
282
|
+
<a
|
|
283
|
+
href="#contact"
|
|
284
|
+
style={{ color: 'white', textDecoration: 'none' }}
|
|
285
|
+
>
|
|
286
|
+
Contact
|
|
287
|
+
</a>
|
|
288
|
+
|
|
289
|
+
<div style={{ paddingTop: '6px' }}>
|
|
290
|
+
<LanguageSwitcher
|
|
291
|
+
useDummyData={true}
|
|
292
|
+
textColor={{ rgba: 'rgb(55, 245, 255)' }}
|
|
293
|
+
textColorHover={{ rgba: 'rgb(255, 255, 255)' }}
|
|
294
|
+
menuBackgroundColor={{ rgba: 'rgba(100, 181, 246, 1)' }}
|
|
295
|
+
menuBackgroundColorHover={{ rgba: 'rgba(0, 160, 240, 1)' }}
|
|
296
|
+
/>
|
|
297
|
+
</div>
|
|
298
|
+
</nav>
|
|
299
|
+
</header>
|
|
300
|
+
|
|
301
|
+
<main style={{ padding: '48px 24px' }}>
|
|
302
|
+
<SBContainer addBackground>
|
|
303
|
+
<h2>Welcome to our multilingual website</h2>
|
|
304
|
+
<p>
|
|
305
|
+
This example shows how the LanguageSwitcher component can be
|
|
306
|
+
integrated into a typical website header with navigation elements.
|
|
307
|
+
</p>
|
|
308
|
+
<p style={{ marginTop: '16px' }}>
|
|
309
|
+
The language switcher is positioned in the top right corner,
|
|
310
|
+
making it easily accessible to users who want to change the site
|
|
311
|
+
language.
|
|
312
|
+
</p>
|
|
313
|
+
</SBContainer>
|
|
314
|
+
</main>
|
|
315
|
+
</SBContainer>
|
|
316
|
+
);
|
|
317
|
+
},
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
export const InteractionStates: Story = {
|
|
321
|
+
name: 'Interaction States',
|
|
322
|
+
render: () => (
|
|
323
|
+
<SBContainer flex direction="column" gap="large">
|
|
324
|
+
<SBContainer addBackground>
|
|
325
|
+
<h4>Hover State</h4>
|
|
326
|
+
<p>Hover over the language switcher button to see the hover effect</p>
|
|
327
|
+
<LanguageSwitcher
|
|
328
|
+
useDummyData={true}
|
|
329
|
+
languageSwitcherSelectText="Select a language"
|
|
330
|
+
/>
|
|
331
|
+
</SBContainer>
|
|
332
|
+
|
|
333
|
+
<SBContainer addBackground>
|
|
334
|
+
<h4>Focus State</h4>
|
|
335
|
+
<p>
|
|
336
|
+
In production, this focus outline appears when navigating with the
|
|
337
|
+
keyboard (Tab key). This Storybook demo auto-applies the focus state
|
|
338
|
+
to show how it looks.
|
|
339
|
+
</p>
|
|
340
|
+
<SBFocusWrapper>
|
|
341
|
+
<LanguageSwitcher
|
|
342
|
+
useDummyData={true}
|
|
343
|
+
languageSwitcherSelectText="Select a language"
|
|
344
|
+
/>
|
|
345
|
+
</SBFocusWrapper>
|
|
346
|
+
</SBContainer>
|
|
347
|
+
</SBContainer>
|
|
348
|
+
),
|
|
349
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { LanguageVariant } from '@hubspot/cms-components';
|
|
2
|
+
import {
|
|
3
|
+
ColorFieldDefaults,
|
|
4
|
+
TextFieldDefaults,
|
|
5
|
+
} from '@hubspot/cms-components/fields';
|
|
6
|
+
import type { CSSVariables } from '../utils/types.js';
|
|
7
|
+
|
|
8
|
+
type ColorFieldValue = typeof ColorFieldDefaults;
|
|
9
|
+
|
|
10
|
+
export type LanguageSwitcherProps = {
|
|
11
|
+
useDummyData?: boolean; // !todo - remove this in PROD
|
|
12
|
+
className?: string;
|
|
13
|
+
style?: CSSVariables;
|
|
14
|
+
color?: ColorFieldValue;
|
|
15
|
+
colorHover?: ColorFieldValue;
|
|
16
|
+
menuBackgroundColorHover?: ColorFieldValue;
|
|
17
|
+
menuBackgroundColor?: ColorFieldValue;
|
|
18
|
+
languageSwitcherSelectText?: string;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export type LanguageOptionsProps = {
|
|
22
|
+
translations: LanguageVariant[];
|
|
23
|
+
menuBackgroundColor?: ColorFieldValue;
|
|
24
|
+
menuBackgroundColorHover?: ColorFieldValue;
|
|
25
|
+
color?: ColorFieldValue;
|
|
26
|
+
colorHover?: ColorFieldValue;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export type ContentFieldsProps = {
|
|
30
|
+
languageSwitcherSelectTextLabel?: string;
|
|
31
|
+
languageSwitcherSelectTextName?: string;
|
|
32
|
+
languageSwitcherSelectTextDefault?: typeof TextFieldDefaults;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export type StyleFieldsProps = {
|
|
36
|
+
colorLabel?: string;
|
|
37
|
+
colorName?: string;
|
|
38
|
+
colorDefault?: ColorFieldValue;
|
|
39
|
+
colorHoverLabel?: string;
|
|
40
|
+
colorHoverName?: string;
|
|
41
|
+
colorHoverDefault?: ColorFieldValue;
|
|
42
|
+
menuBackgroundColorLabel?: string;
|
|
43
|
+
menuBackgroundColorName?: string;
|
|
44
|
+
menuBackgroundColorDefault?: ColorFieldValue;
|
|
45
|
+
menuBackgroundColorHoverLabel?: string;
|
|
46
|
+
menuBackgroundColorHoverName?: string;
|
|
47
|
+
menuBackgroundColorHoverDefault?: ColorFieldValue;
|
|
48
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { LanguageVariant } from '@hubspot/cms-components';
|
|
2
|
+
|
|
3
|
+
// Define interfaces for styled components props
|
|
4
|
+
|
|
5
|
+
type TranslationsArrayAsObject = Record<string, LanguageVariant>;
|
|
6
|
+
|
|
7
|
+
type GetLanguageDisplayNameArgs = {
|
|
8
|
+
currentPageLanguage: string;
|
|
9
|
+
translationsArrayAsObject: TranslationsArrayAsObject | null;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// Shared utility functions
|
|
13
|
+
export const getLanguageDisplayName = (args: GetLanguageDisplayNameArgs) => {
|
|
14
|
+
const { currentPageLanguage, translationsArrayAsObject } = args;
|
|
15
|
+
|
|
16
|
+
if (!translationsArrayAsObject || !currentPageLanguage) {
|
|
17
|
+
return '';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
translationsArrayAsObject[currentPageLanguage]?.languageDisplayName
|
|
22
|
+
.LOCALIZED ?? ''
|
|
23
|
+
);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const createTranslationsArrayAsObject = (
|
|
27
|
+
translations: LanguageVariant[]
|
|
28
|
+
): TranslationsArrayAsObject | null => {
|
|
29
|
+
// Early return if no translations
|
|
30
|
+
if (!translations || translations.length <= 1) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return translations.reduce<TranslationsArrayAsObject>((acc, translation) => {
|
|
35
|
+
acc[translation.languageCode] = translation;
|
|
36
|
+
return acc;
|
|
37
|
+
}, {});
|
|
38
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { LinkField } from '@hubspot/cms-components/fields';
|
|
2
2
|
import { ContentFieldsProps } from './types.js';
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
const ContentFields = ({
|
|
5
5
|
linkLabel = 'Link',
|
|
6
6
|
linkName = 'link',
|
|
7
7
|
linkDefault = {
|
|
@@ -11,6 +11,8 @@ export default function ContentFields({
|
|
|
11
11
|
href: 'https://www.hubspot.com',
|
|
12
12
|
},
|
|
13
13
|
},
|
|
14
|
-
}: ContentFieldsProps) {
|
|
14
|
+
}: ContentFieldsProps) => {
|
|
15
15
|
return <LinkField label={linkLabel} name={linkName} default={linkDefault} />;
|
|
16
|
-
}
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export default ContentFields;
|
|
@@ -2,11 +2,11 @@ import { ChoiceField } from '@hubspot/cms-components/fields';
|
|
|
2
2
|
import { StyleFieldsProps } from './types.js';
|
|
3
3
|
|
|
4
4
|
// !todo: may not need later, but keeping for now in case we have variant system for links
|
|
5
|
-
|
|
5
|
+
const StyleFields = ({
|
|
6
6
|
linkVariantLabel = 'Link variant',
|
|
7
7
|
linkVariantName = 'linkVariant',
|
|
8
8
|
linkVariantDefault = 'primary',
|
|
9
|
-
}: StyleFieldsProps) {
|
|
9
|
+
}: StyleFieldsProps) => {
|
|
10
10
|
return (
|
|
11
11
|
<>
|
|
12
12
|
<ChoiceField
|
|
@@ -21,4 +21,6 @@ export default function StyleFields({
|
|
|
21
21
|
/>
|
|
22
22
|
</>
|
|
23
23
|
);
|
|
24
|
-
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export default StyleFields;
|
|
@@ -8,4 +8,14 @@
|
|
|
8
8
|
color: var(--hscl-link-color-hover);
|
|
9
9
|
text-decoration: var(--hscl-link-textDecoration-hover, underline);
|
|
10
10
|
}
|
|
11
|
+
|
|
12
|
+
&:focus-visible {
|
|
13
|
+
outline: var(--hscl-link-outlineWidth-focus, 2px) solid var(--hscl-link-outlineColor-focus, currentColor);
|
|
14
|
+
outline-offset: var(--hscl-link-outlineOffset-focus, 2px);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
&:active {
|
|
18
|
+
color: var(--hscl-link-color-active, var(--hscl-link-color));
|
|
19
|
+
text-decoration: var(--hscl-link-textDecoration-active, var(--hscl-link-textDecoration, underline));
|
|
20
|
+
}
|
|
11
21
|
}
|
|
@@ -3,34 +3,43 @@ import ContentFields from './ContentFields.js';
|
|
|
3
3
|
import StyleFields from './StyleFields.js';
|
|
4
4
|
import cx from '../utils/classname.js';
|
|
5
5
|
import type { CSSVariables } from '../utils/types.js';
|
|
6
|
-
import { LinkProps } from './types.js';
|
|
6
|
+
import { LinkProps, BaseLinkProps, LinkHTMLProps } from './types.js';
|
|
7
7
|
import {
|
|
8
8
|
getLinkRel,
|
|
9
9
|
getLinkFieldTarget,
|
|
10
10
|
getLinkFieldRel,
|
|
11
11
|
} from '../utils/linkField.js';
|
|
12
12
|
|
|
13
|
-
const
|
|
14
|
-
variant = 'primary',
|
|
15
|
-
className = '',
|
|
16
|
-
style = {},
|
|
17
|
-
children,
|
|
18
|
-
...props
|
|
19
|
-
}: LinkProps) => {
|
|
13
|
+
const getLinkValues = (props: Omit<LinkProps, keyof BaseLinkProps>) => {
|
|
20
14
|
let href: string;
|
|
21
15
|
let target: '_self' | '_blank' | '_parent' | '_top';
|
|
22
16
|
let relValues: string | undefined;
|
|
17
|
+
let anchorProps: LinkHTMLProps;
|
|
23
18
|
|
|
19
|
+
// if linkField was passed in, use it for attrs; otherwise, build up link attrs from props
|
|
24
20
|
if ('linkField' in props && props.linkField) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
21
|
+
const { linkField, ...otherProps } = props;
|
|
22
|
+
href = linkField.url?.href || '';
|
|
23
|
+
target = getLinkFieldTarget(linkField);
|
|
24
|
+
relValues = getLinkFieldRel(linkField);
|
|
25
|
+
anchorProps = otherProps;
|
|
28
26
|
} else {
|
|
29
|
-
href =
|
|
30
|
-
target = props.target || '_self';
|
|
27
|
+
({ href = '', target = '_self', ...anchorProps } = props);
|
|
31
28
|
relValues = getLinkRel(target, props.rel);
|
|
32
29
|
}
|
|
33
30
|
|
|
31
|
+
return { href, target, rel: relValues, anchorProps };
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const LinkComponent = ({
|
|
35
|
+
variant = 'primary',
|
|
36
|
+
className = '',
|
|
37
|
+
style = {},
|
|
38
|
+
children,
|
|
39
|
+
...rest
|
|
40
|
+
}: LinkProps) => {
|
|
41
|
+
const { href, target, rel, anchorProps } = getLinkValues(rest);
|
|
42
|
+
|
|
34
43
|
const defaultClasses = styles.link;
|
|
35
44
|
const combinedClasses = cx(defaultClasses, className);
|
|
36
45
|
|
|
@@ -45,7 +54,8 @@ const LinkComponent = ({
|
|
|
45
54
|
style={{ ...cssVariables, ...style }}
|
|
46
55
|
href={href}
|
|
47
56
|
target={target}
|
|
48
|
-
rel={
|
|
57
|
+
rel={rel}
|
|
58
|
+
{...anchorProps}
|
|
49
59
|
>
|
|
50
60
|
{children}
|
|
51
61
|
</a>
|
|
@@ -2,13 +2,14 @@ import type { Meta, StoryObj } from '@storybook/react';
|
|
|
2
2
|
import Link from '../index.js';
|
|
3
3
|
import { LinkProps } from '../types.js';
|
|
4
4
|
import { withLinkStyles } from './LinkDecorator.js';
|
|
5
|
-
import { SBContainer } from '@sb-utils
|
|
5
|
+
import { SBContainer, SBFocusWrapper } from '@sb-utils';
|
|
6
6
|
import { SBCard } from '@sb-utils/SBCard.js';
|
|
7
|
+
import type { CSSVariables } from '../../utils/types.js';
|
|
7
8
|
|
|
8
|
-
const removeUnderlineStyle = {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
9
|
+
const removeUnderlineStyle: CSSVariables = {
|
|
10
|
+
'--hscl-link-textDecoration': 'none',
|
|
11
|
+
'--hscl-link-textDecoration-hover': 'none',
|
|
12
|
+
};
|
|
12
13
|
|
|
13
14
|
const meta: Meta<LinkProps> = {
|
|
14
15
|
title: 'Component Library/Link',
|
|
@@ -170,3 +171,32 @@ export const InContext: Story = {
|
|
|
170
171
|
</SBContainer>
|
|
171
172
|
),
|
|
172
173
|
};
|
|
174
|
+
|
|
175
|
+
export const InteractionStates: Story = {
|
|
176
|
+
name: 'Interaction States',
|
|
177
|
+
render: () => (
|
|
178
|
+
<SBContainer flex direction="column" gap="large">
|
|
179
|
+
<SBContainer addBackground>
|
|
180
|
+
<h4>Hover State</h4>
|
|
181
|
+
<p>Hover over the link to see the hover effect</p>
|
|
182
|
+
<Link href="https://www.hubspot.com" variant="primary">
|
|
183
|
+
Hover me
|
|
184
|
+
</Link>
|
|
185
|
+
</SBContainer>
|
|
186
|
+
|
|
187
|
+
<SBContainer addBackground>
|
|
188
|
+
<h4>Focus State</h4>
|
|
189
|
+
<p>
|
|
190
|
+
In production, this focus outline appears when navigating with the
|
|
191
|
+
keyboard (Tab key). This Storybook demo auto-applies the focus state
|
|
192
|
+
to show how it looks.
|
|
193
|
+
</p>
|
|
194
|
+
<SBFocusWrapper>
|
|
195
|
+
<Link href="https://www.hubspot.com" variant="primary">
|
|
196
|
+
Focused link
|
|
197
|
+
</Link>
|
|
198
|
+
</SBFocusWrapper>
|
|
199
|
+
</SBContainer>
|
|
200
|
+
</SBContainer>
|
|
201
|
+
),
|
|
202
|
+
};
|
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import type { Decorator } from '@storybook/react';
|
|
2
|
+
import type { CSSVariables } from '../../utils/types.js';
|
|
3
|
+
|
|
4
|
+
const defaultLinkStyles: CSSVariables = {
|
|
5
|
+
'--hscl-link-color-primary': '#0066cc',
|
|
6
|
+
'--hscl-link-color-secondary': '#516f90',
|
|
7
|
+
'--hscl-link-color-tertiary': '#7c98b6',
|
|
8
|
+
'--hscl-link-color-hover-primary': '#0052a3',
|
|
9
|
+
'--hscl-link-color-hover-secondary': '#33475b',
|
|
10
|
+
'--hscl-link-color-hover-tertiary': '#516f90',
|
|
11
|
+
};
|
|
2
12
|
|
|
3
13
|
export const withLinkStyles: Decorator = Story => (
|
|
4
|
-
<div>
|
|
14
|
+
<div style={defaultLinkStyles}>
|
|
5
15
|
<Story />
|
|
6
16
|
</div>
|
|
7
17
|
);
|