@hubspot/cms-component-library 0.1.0-alpha.1

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.
Files changed (37) hide show
  1. package/README.md +3 -0
  2. package/cli/commands/customize.ts +145 -0
  3. package/cli/commands/help.ts +56 -0
  4. package/cli/commands/version.ts +12 -0
  5. package/cli/index.ts +42 -0
  6. package/cli/tests/commands.test.ts +128 -0
  7. package/cli/tests/get-file.test.ts +82 -0
  8. package/cli/tests/version-integration.test.ts +39 -0
  9. package/cli/utils/cli-metadata.ts +9 -0
  10. package/cli/utils/component-naming.ts +76 -0
  11. package/cli/utils/components.ts +74 -0
  12. package/cli/utils/file-operations.ts +158 -0
  13. package/cli/utils/logging.ts +13 -0
  14. package/cli/utils/prompts.ts +80 -0
  15. package/cli/utils/version.ts +33 -0
  16. package/components/componentLibrary/Button/index.module.scss +9 -0
  17. package/components/componentLibrary/Button/index.tsx +83 -0
  18. package/components/componentLibrary/Button/scaffolds/fields.tsx.template +70 -0
  19. package/components/componentLibrary/Button/scaffolds/index.ts.template +95 -0
  20. package/components/componentLibrary/Heading/index.module.scss +9 -0
  21. package/components/componentLibrary/Heading/index.tsx +34 -0
  22. package/components/componentLibrary/Heading/scaffolds/fields.tsx.template +62 -0
  23. package/components/componentLibrary/Heading/scaffolds/index.ts.template +46 -0
  24. package/components/componentLibrary/index.ts +1 -0
  25. package/components/componentLibrary/styles/_component-base.scss +246 -0
  26. package/components/componentLibrary/types/index.ts +308 -0
  27. package/components/componentLibrary/utils/chainApi/choiceFieldGenerator.tsx +64 -0
  28. package/components/componentLibrary/utils/chainApi/index.ts +115 -0
  29. package/components/componentLibrary/utils/chainApi/labelGenerator.ts +76 -0
  30. package/components/componentLibrary/utils/chainApi/stateManager.ts +178 -0
  31. package/components/componentLibrary/utils/classname.ts +40 -0
  32. package/components/componentLibrary/utils/createConditionalClasses.ts +44 -0
  33. package/components/componentLibrary/utils/createHsclComponent.tsx +167 -0
  34. package/components/componentLibrary/utils/propResolution/createCssVariables.ts +58 -0
  35. package/components/componentLibrary/utils/propResolution/propResolutionUtils.ts +113 -0
  36. package/components/componentLibrary/utils/storybook/standardArgs.ts +607 -0
  37. package/package.json +62 -0
@@ -0,0 +1,158 @@
1
+ import { promises as fs, readFileSync } from 'fs';
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ export const __dirname = path.dirname(__filename);
7
+
8
+ export const getFileContents = (filePath: string): string => {
9
+ const fullPath = path.join(__dirname, filePath);
10
+ try {
11
+ return readFileSync(fullPath, 'utf8');
12
+ } catch (error: unknown) {
13
+ const errorMessage = `Error reading file at path "${fullPath}": ${
14
+ error instanceof Error ? error.message : 'Unknown error occurred'
15
+ }`;
16
+
17
+ throw new Error(errorMessage);
18
+ }
19
+ };
20
+
21
+ /**
22
+ * Get the components directory in the current working directory
23
+ * Creates it if it doesn't exist
24
+ */
25
+ async function getComponentsDirectory(): Promise<string> {
26
+ const cwd = process.cwd();
27
+ const componentsPath = path.join(cwd, 'components');
28
+
29
+ // Create components directory if it doesn't exist
30
+ await fs.mkdir(componentsPath, { recursive: true });
31
+
32
+ return componentsPath;
33
+ }
34
+
35
+ interface CopyScaffoldsOptions {
36
+ sourceComponent: string;
37
+ targetName: string;
38
+ }
39
+
40
+ /**
41
+ * Copy scaffold files from a source component, replacing template variables
42
+ * @param sourceComponent - The name of the component to copy scaffolds from
43
+ * @param targetName - The new name (prefix + component name) to replace __NC__
44
+ * @param outputDir - Optional output directory, defaults to current working directory
45
+ */
46
+ export async function copyScaffolds({
47
+ sourceComponent,
48
+ targetName,
49
+ }: CopyScaffoldsOptions): Promise<void> {
50
+ const sourceDir = path.join(
51
+ __dirname,
52
+ '../../components/componentLibrary',
53
+ sourceComponent,
54
+ 'scaffolds'
55
+ );
56
+
57
+ // Check if scaffolds directory exists
58
+ try {
59
+ await fs.access(sourceDir);
60
+ } catch {
61
+ throw new Error(
62
+ `Scaffolds directory not found for component: ${sourceComponent}`
63
+ );
64
+ }
65
+
66
+ // Get components directory and create hscl subdirectory
67
+ const componentsDir = await getComponentsDirectory();
68
+ const hsclDirectory = path.join(componentsDir, 'hscl');
69
+ const targetDir = path.join(hsclDirectory, targetName);
70
+
71
+ // Check if target directory already exists
72
+ try {
73
+ await fs.access(targetDir);
74
+ throw new Error(
75
+ `Component "${targetName}" already exists in /components/hscl/. Please choose a different prefix or remove the existing component.`
76
+ );
77
+ } catch (error: unknown) {
78
+ // If access fails with ENOENT, the directory doesn't exist (which is what we want)
79
+ // For any other error, re-throw it
80
+ if (
81
+ error instanceof Error &&
82
+ error.message &&
83
+ !error.message.includes('ENOENT')
84
+ ) {
85
+ throw error;
86
+ }
87
+ // For ENOENT errors, continue (directory doesn't exist, which is good)
88
+ }
89
+
90
+ await fs.mkdir(targetDir, { recursive: true });
91
+
92
+ // Read all files in scaffolds directory
93
+ const files = await fs.readdir(sourceDir);
94
+ const templateFiles = files.filter(file => file.endsWith('.template'));
95
+
96
+ await Promise.all(
97
+ templateFiles.map(async templateFile => {
98
+ const sourceFilePath = path.join(sourceDir, templateFile);
99
+ const targetFileName = templateFile.replace('.template', '');
100
+ const targetFilePath = path.join(targetDir, targetFileName);
101
+
102
+ // Read template content using the get-file utility
103
+ const relativePath = path.relative(__dirname, sourceFilePath);
104
+ const templateContent = getFileContents(relativePath);
105
+
106
+ // Replace template variables
107
+ const processedContent = templateContent.replace(/__NC__/g, targetName);
108
+
109
+ // Write to target file
110
+ await fs.writeFile(targetFilePath, processedContent, 'utf8');
111
+ })
112
+ );
113
+ }
114
+
115
+ /**
116
+ * Copy scaffolds for multiple components
117
+ */
118
+ export async function copyMultipleScaffolds({
119
+ sourceComponents,
120
+ prefix,
121
+ }: {
122
+ sourceComponents: string[];
123
+ prefix: string;
124
+ }): Promise<{
125
+ successful: string[];
126
+ skipped: string[];
127
+ errors: Array<{ component: string; error: string }>;
128
+ }> {
129
+ const results = {
130
+ successful: [],
131
+ skipped: [],
132
+ errors: [],
133
+ };
134
+
135
+ for (const component of sourceComponents) {
136
+ const targetName = `${prefix}${component}`;
137
+ try {
138
+ await copyScaffolds({
139
+ sourceComponent: component,
140
+ targetName,
141
+ });
142
+ results.successful.push(component);
143
+ } catch (error: unknown) {
144
+ const errorMessage =
145
+ error instanceof Error ? error.message : String(error);
146
+
147
+ // Check if this is a "component already exists" error
148
+ if (errorMessage.includes('already exists')) {
149
+ results.skipped.push(component);
150
+ } else {
151
+ results.errors.push({ component, error: errorMessage });
152
+ console.error(`❌ Error creating ${targetName}: ${errorMessage}`);
153
+ }
154
+ }
155
+ }
156
+
157
+ return results;
158
+ }
@@ -0,0 +1,13 @@
1
+ import chalk from 'chalk';
2
+
3
+ export const logSuccess = (message: string): void => {
4
+ console.log(chalk.green(message));
5
+ };
6
+
7
+ export const logError = (message: string): void => {
8
+ console.error(chalk.red(message));
9
+ };
10
+
11
+ export const logWarning = (message: string): void => {
12
+ console.error(chalk.magenta(message));
13
+ };
@@ -0,0 +1,80 @@
1
+ import prompts from 'prompts';
2
+ import chalk from 'chalk';
3
+
4
+ // Helper for styling help text
5
+ const helpText = (text: string): string => chalk.dim.gray(text);
6
+
7
+ interface SelectPromptOptions {
8
+ type: 'select' | 'multiselect';
9
+ message: string;
10
+ choices: (string | { title: string; value: string })[];
11
+ helpMessage?: string;
12
+ }
13
+
14
+ interface TextPromptOptions {
15
+ message: string;
16
+ defaultValue: string;
17
+ }
18
+
19
+ // Reusable prompt functions
20
+ export const selectPrompt = async ({
21
+ type,
22
+ message,
23
+ choices,
24
+ helpMessage,
25
+ }: SelectPromptOptions) => {
26
+ const fullMessage = helpMessage
27
+ ? `${message} ${helpText(helpMessage)}`
28
+ : message;
29
+
30
+ return await prompts({
31
+ type: type,
32
+ name: 'value',
33
+ message: fullMessage,
34
+ choices: choices.map(choice =>
35
+ typeof choice === 'string' ? { title: choice, value: choice } : choice
36
+ ),
37
+ });
38
+ };
39
+
40
+ export const multiselectPrompt = async ({
41
+ message,
42
+ choices,
43
+ helpMessage,
44
+ }: Omit<SelectPromptOptions, 'type'>): Promise<{ value: string[] }> => {
45
+ return await selectPrompt({
46
+ type: 'multiselect',
47
+ message,
48
+ helpMessage,
49
+ choices: choices.map(choice =>
50
+ typeof choice === 'string' ? { title: choice, value: choice } : choice
51
+ ),
52
+ });
53
+ };
54
+
55
+ export const textPromptWithDefault = async ({
56
+ message,
57
+ defaultValue,
58
+ }: TextPromptOptions): Promise<string> => {
59
+ const { value } = await prompts({
60
+ type: 'text',
61
+ name: 'value',
62
+ message: `${message} ${helpText(`(default: ${defaultValue})`)}`,
63
+ });
64
+
65
+ return value || defaultValue;
66
+ };
67
+
68
+ export const promptForNamingPrefix = async (
69
+ defaultValue: string,
70
+ numberOfComponents: number
71
+ ): Promise<string> => {
72
+ const isMultipleComponents = numberOfComponents > 1;
73
+
74
+ return await textPromptWithDefault({
75
+ message: `What prefix should we use for your new component${
76
+ isMultipleComponents ? 's' : ''
77
+ }?`,
78
+ defaultValue,
79
+ });
80
+ };
@@ -0,0 +1,33 @@
1
+ import { getFileContents } from './file-operations.js';
2
+
3
+ interface PackageJson {
4
+ name: string;
5
+ version: string;
6
+ description?: string;
7
+ license?: string;
8
+ scripts?: Record<string, string>;
9
+ repository?: {
10
+ type: string;
11
+ url: string;
12
+ };
13
+ dependencies?: Record<string, string>;
14
+ devDependencies?: Record<string, string>;
15
+ author?: string;
16
+ 'lint-staged'?: Record<string, string[]>;
17
+ resolutions?: Record<string, string>;
18
+ }
19
+
20
+ export const getVersion = (): string => {
21
+ try {
22
+ const packageJson: PackageJson = JSON.parse(
23
+ getFileContents('../../package.json')
24
+ );
25
+ return packageJson.version;
26
+ } catch (error) {
27
+ console.error(
28
+ 'Error reading package.json:',
29
+ error instanceof Error ? error.message : error
30
+ );
31
+ return '1.0.0';
32
+ }
33
+ };
@@ -0,0 +1,9 @@
1
+ @use '../styles/_component-base';
2
+
3
+ @layer hscl-library {
4
+ .button {
5
+
6
+ @include component-base.hscl-component-conditional('button');
7
+ @include component-base.hscl-component-hover-conditional('button');
8
+ }
9
+ }
@@ -0,0 +1,83 @@
1
+ /* eslint-disable */
2
+
3
+ import createHsclComponent from '../utils/createHsclComponent.js';
4
+ import styles from './index.module.scss';
5
+ import { BaseAndInternalComponentProps } from '../types/index.js';
6
+
7
+ // Base props shared by all variants
8
+ type BaseButtonProps = BaseAndInternalComponentProps & {
9
+ disabled?: boolean;
10
+ children?: React.ReactNode;
11
+ };
12
+
13
+ // Button variant props
14
+ type ButtonVariantProps = {
15
+ buttonType: 'button';
16
+ onClick?: () => void;
17
+ };
18
+
19
+ // Link variant props
20
+ type LinkVariantProps = {
21
+ buttonType: 'link';
22
+ href?: string;
23
+ target?: string;
24
+ rel?: string;
25
+ };
26
+
27
+ // Main component props using discriminated union
28
+ export type ButtonProps = BaseButtonProps &
29
+ (ButtonVariantProps | LinkVariantProps);
30
+
31
+ const componentName = 'button';
32
+
33
+ const Component = (props: ButtonProps) => {
34
+ // Extract shared/base props that are always consumed
35
+ const {
36
+ buttonType = 'link',
37
+ hsclInternal: { hsclProcessedClasses, hsclProcessedStyles },
38
+ ...rest
39
+ } = props;
40
+
41
+ // Shared props for both elements
42
+ const sharedProps = {
43
+ className: hsclProcessedClasses,
44
+ style: hsclProcessedStyles,
45
+ };
46
+
47
+ function renderButton(
48
+ props: Omit<ButtonVariantProps, 'buttonType' | 'hsclInternal'> &
49
+ Partial<BaseButtonProps>
50
+ ) {
51
+ const { onClick, disabled = false, children, ...rest } = props;
52
+ return (
53
+ <button {...sharedProps} onClick={onClick} disabled={disabled} {...rest}>
54
+ {children}
55
+ </button>
56
+ );
57
+ }
58
+
59
+ function renderLink(
60
+ props: Omit<LinkVariantProps, 'buttonType' | 'hsclInternal'> &
61
+ Partial<BaseButtonProps>
62
+ ) {
63
+ const { href = '', target = '_self', rel = '', children, ...rest } = props;
64
+ return (
65
+ <a {...sharedProps} href={href} target={target} rel={rel} {...rest}>
66
+ {children}
67
+ </a>
68
+ );
69
+ }
70
+
71
+ // TypeScript properly discriminates based on buttonType
72
+ if (buttonType === 'button') return renderButton(rest);
73
+
74
+ // For link variant
75
+ return renderLink(rest);
76
+ };
77
+
78
+ Component.hsclComponentName = componentName;
79
+ Component.cssModule = styles;
80
+
81
+ const Button = createHsclComponent(Component);
82
+
83
+ export default Button;
@@ -0,0 +1,70 @@
1
+ import { LinkField, TextField } from '@hubspot/cms-components/fields'
2
+ import __NC__ from './buttonIndex.js';
3
+
4
+ export type __NC__ContentFieldsProps = {
5
+ buttonTextDefault?: string;
6
+ buttonTextLabel?: string;
7
+ buttonTextName?: string;
8
+ buttonLinkLabel?: string;
9
+ buttonLinkName?: string;
10
+ buttonLinkDefault?: {url: {type: 'EXTERNAL', content_id: number, href: string}};
11
+ showButtonLink?: boolean;
12
+ }
13
+
14
+ export function contentFields(props: __NC__ContentFieldsProps) {
15
+ const { buttonTextDefault, buttonTextLabel, buttonTextName, buttonLinkLabel, buttonLinkName, buttonLinkDefault, showButtonLink = true } = props;
16
+
17
+ return (
18
+ <>
19
+ <TextField
20
+ label={buttonTextLabel || 'Button Text'}
21
+ name={buttonTextName || 'buttonText'}
22
+ default={buttonTextDefault || 'Button Text'}
23
+ />
24
+ {showButtonLink && (
25
+ <LinkField
26
+ label={buttonLinkLabel || 'Button Link'}
27
+ name={buttonLinkName || 'buttonLink'}
28
+ default={buttonLinkDefault || {url: {type: 'EXTERNAL', content_id: 0, href: 'https://www.google.com'}}}
29
+ />
30
+ )}
31
+ </>
32
+ )
33
+ }
34
+
35
+ export type __NC__StyleFieldsProps = {
36
+ buttonVariantLabel?: string;
37
+ buttonVariantName?: string;
38
+ buttonVariantDefault?: string;
39
+ buttonSizeLabel?: string;
40
+ buttonSizeName?: string;
41
+ buttonSizeDefault?: string;
42
+ buttonBorderRadiusLabel?: string;
43
+ buttonBorderRadiusName?: string;
44
+ buttonBorderRadiusDefault?: string;
45
+ }
46
+
47
+ export function styleFields(props: __NC__StyleFieldsProps) {
48
+ const { buttonVariantLabel, buttonVariantName, buttonVariantDefault, buttonSizeLabel, buttonSizeName, buttonSizeDefault, buttonBorderRadiusLabel, buttonBorderRadiusName, buttonBorderRadiusDefault } = props;
49
+
50
+ return (
51
+ <>
52
+ <__NC__.DimensionChoiceFields.variant
53
+ label={buttonVariantLabel || 'Variant'}
54
+ name={buttonVariantName || 'buttonVariant'}
55
+ default={buttonVariantDefault || 'solid'}
56
+ />
57
+ <__NC__.DimensionChoiceFields.size
58
+ label={buttonSizeLabel || 'Size'}
59
+ name={buttonSizeName || 'buttonSize'}
60
+ default={buttonSizeDefault || 'medium'}
61
+ />
62
+ <__NC__.DimensionChoiceFields.borderRadius
63
+ label={buttonBorderRadiusLabel || 'Border Radius'}
64
+ name={buttonBorderRadiusName || 'buttonBorderRadius'}
65
+ default={buttonBorderRadiusDefault || 'medium'}
66
+ />
67
+ </>
68
+ )
69
+ }
70
+
@@ -0,0 +1,95 @@
1
+ import Button, { ButtonProps } from '@hubspot/cms-component-library/Button';
2
+ import { createComponentInstance } from '@hubspot/cms-component-library';
3
+ import { __NC__ContentFieldsProps, __NC__StyleFieldsProps, contentFields, styleFields } from './fields.js';
4
+
5
+ export type __NC__Props = ButtonProps & {
6
+ variant: 'solid' | 'outline' | 'unstyled';
7
+ size: 'small' | 'medium' | 'large' | 'xlarge';
8
+ borderRadius: 'none' | 'small' | 'medium' | 'large' | 'full';
9
+ };
10
+
11
+ const __NC__ = createComponentInstance<__NC__Props>(Button)
12
+ .withContentFields<__NC__ContentFieldsProps>(contentFields)
13
+ .withStyleFields<__NC__StyleFieldsProps>(styleFields);
14
+
15
+ const sharedVariantProps = {
16
+ display: 'inline-block',
17
+ textDecoration: 'none',
18
+ }
19
+
20
+ __NC__.setDimension('variant')
21
+ .setOption('solid')
22
+ .setProps({
23
+ backgroundColor: 'black',
24
+ color: 'white',
25
+ hoverBackgroundColor: '#262626',
26
+ ...sharedVariantProps
27
+ })
28
+ .setOption('outline')
29
+ .setProps({
30
+ backgroundColor: 'transparent',
31
+ color: 'black',
32
+ border: '1px solid black',
33
+ hoverBackgroundColor: '#E6E6E6',
34
+ ...sharedVariantProps
35
+ })
36
+ .setOption('unstyled')
37
+ .setProps({})
38
+ .createDimensionChoiceField();
39
+
40
+ const sharedSizeProps = {
41
+ fontWeight: '600',
42
+ lineHeight: '1.3',
43
+ }
44
+
45
+ __NC__.setDimension('size')
46
+ .setOption('small')
47
+ .setProps({
48
+ fontSize: '12px',
49
+ padding: '12px 16px',
50
+ ...sharedSizeProps
51
+ })
52
+ .setOption('medium')
53
+ .setProps({
54
+ fontSize: '16px',
55
+ padding: '16px 20px',
56
+ ...sharedSizeProps
57
+ })
58
+ .setOption('large')
59
+ .setProps({
60
+ fontSize: '24px',
61
+ padding: '20px 24px',
62
+ ...sharedSizeProps
63
+ })
64
+ .setOption('extraLarge')
65
+ .setProps({
66
+ fontSize: '30px',
67
+ padding: '24px 30px',
68
+ ...sharedSizeProps
69
+ })
70
+ .createDimensionChoiceField();
71
+
72
+ __NC__.setDimension('borderRadius')
73
+ .setOption('none')
74
+ .setProps({
75
+ borderRadius: '0',
76
+ })
77
+ .setOption('small')
78
+ .setProps({
79
+ borderRadius: '4px',
80
+ })
81
+ .setOption('medium')
82
+ .setProps({
83
+ borderRadius: '8px',
84
+ })
85
+ .setOption('large')
86
+ .setProps({
87
+ borderRadius: '12px',
88
+ })
89
+ .setOption('full')
90
+ .setProps({
91
+ borderRadius: '50px',
92
+ })
93
+ .createDimensionChoiceField();
94
+
95
+ export default __NC__;
@@ -0,0 +1,9 @@
1
+ @use '../styles/_component-base';
2
+
3
+ @layer hscl-library {
4
+ .heading {
5
+
6
+ @include component-base.hscl-component-conditional('heading');
7
+ @include component-base.hscl-component-hover-conditional('heading');
8
+ }
9
+ }
@@ -0,0 +1,34 @@
1
+ /* eslint-disable */
2
+
3
+ import createHsclComponent from '../utils/createHsclComponent.js';
4
+ import styles from './index.module.scss';
5
+ import { BaseAndInternalComponentProps } from '../types/index.js';
6
+
7
+ export type HeadingProps = BaseAndInternalComponentProps & {
8
+ text: string;
9
+ headingLevel: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
10
+ };
11
+
12
+ const componentName = 'heading';
13
+
14
+ const Component = (props: HeadingProps) => {
15
+ const {
16
+ headingLevel = 'h1',
17
+ hsclInternal: { hsclProcessedClasses, hsclProcessedStyles },
18
+ } = props;
19
+
20
+ const HeadingLevel = headingLevel;
21
+
22
+ return (
23
+ <HeadingLevel style={hsclProcessedStyles} className={hsclProcessedClasses}>
24
+ {props.text}
25
+ </HeadingLevel>
26
+ );
27
+ };
28
+
29
+ Component.hsclComponentName = componentName;
30
+ Component.cssModule = styles;
31
+
32
+ const Heading = createHsclComponent(Component);
33
+
34
+ export default Heading;
@@ -0,0 +1,62 @@
1
+ import React from 'react'
2
+ import { __NC__ } from './index.js'
3
+ import { TextField, AlignmentField } from '@hubspot/cms-components/fields'
4
+
5
+ export type __NC__ContentFieldsProps = {
6
+ HeadingDefault?: string;
7
+ HeadingLabel?: string;
8
+ HeadingName?: string;
9
+ HeadingLevelLabel?: string;
10
+ HeadingLevelName?: string;
11
+ HeadingLevelDefault
12
+ }
13
+
14
+ export function contentFields(props: __NC__ContentFieldsProps) {
15
+ const { HeadingDefault, HeadingLabel, HeadingName, HeadingLevelLabel, HeadingLevelName, HeadingLevelDefault } = props;
16
+
17
+ return (
18
+ <>
19
+ <TextField
20
+ label={HeadingLabel || 'Heading Text'}
21
+ name={HeadingName || 'headingText'}
22
+ default={HeadingDefault || 'Default Heading Text'}
23
+ />
24
+ <ChoiceField
25
+ label={HeadingLevelLabel || "Heading Level"}
26
+ name={HeadingLevelName || "headingLevel"}
27
+ choices={[['h1', 'h1'],['h2', 'h2'],['h3', 'h3'],['h4', 'h4'],['h5', 'h5'],['h6', 'h6']]}
28
+ default={HeadingLevelDefault || 'h1'}
29
+ />
30
+ </>
31
+ )
32
+ }
33
+
34
+ export type __NC__StyleFieldsProps = {
35
+ displayAsLabel?: string;
36
+ displayAsName?: string;
37
+ displayAsDefault?: string;
38
+ alignmentLabel?: string;
39
+ alignmentName?: string;
40
+ alignmentDefault?: 'LEFT' | 'CENTER' | 'RIGHT';
41
+ }
42
+
43
+ export function styleFields(props: __NC__StyleFieldsProps) {
44
+ const { displayAsLabel, displayAsName, displayAsDefault, alignmentLabel, alignmentName, alignmentDefault } = props;
45
+
46
+ return (
47
+ <>
48
+ <AlignmentField
49
+ label={alignmentLabel || 'Heading Alignment'}
50
+ default={{ horizontal_align: alignmentDefault || 'LEFT' }}
51
+ name={alignmentName || 'headingAlignment'}
52
+ alignmentDirection="HORIZONTAL"
53
+ />
54
+ <__NC__.DimensionChoiceFields.displayAs
55
+ label={displayAsLabel || 'Display as'}
56
+ default={displayAsDefault || 'h1'}
57
+ name={displayAsName || 'headingDisplayAs'}
58
+ />
59
+ </>
60
+ )
61
+ }
62
+
@@ -0,0 +1,46 @@
1
+ import { createComponentInstance } from '@hubspot/cms-component-library/';
2
+ import Heading from '@hubspot/cms-component-library/Heading'
3
+ import { HeadingProps } from '@hubspot/cms-component-library/Heading'
4
+ import { __NC__ContentFieldsProps, __NC__StyleFieldsProps, contentFields, styleFields } from './fields.js';
5
+
6
+ export type __NC__Props = HeadingProps & {
7
+ displayAs: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
8
+ };
9
+
10
+ export const __NC__ = createComponentInstance<__NC__Props>(Heading)
11
+ .withContentFields<__NC__ContentFieldsProps>(contentFields)
12
+ .withStyleFields<__NC__StyleFieldsProps>(styleFields);
13
+
14
+ __NC__.setDimension('displayAs')
15
+ .setOption('h1')
16
+ .setProps({
17
+ fontSize: '2.125rem',
18
+ fontWeight: '700',
19
+ })
20
+ .setOption('h2')
21
+ .setProps({
22
+ fontSize: '1.875rem',
23
+ fontWeight: '700',
24
+ })
25
+ .setOption('h3')
26
+ .setProps({
27
+ fontSize: '1.5rem',
28
+ fontWeight: '700',
29
+ })
30
+ .setOption('h4')
31
+ .setProps({
32
+ fontSize: '1.25rem',
33
+ fontWeight: '700',
34
+ })
35
+ .setOption('h5')
36
+ .setProps({
37
+ fontSize: '1rem',
38
+ fontWeight: '700',
39
+ })
40
+ .setOption('h6')
41
+ .setProps({
42
+ fontSize: '0.875rem',
43
+ fontWeight: '700',
44
+ })
45
+ .createDimensionChoiceField();
46
+