Skip to content

Commit

Permalink
feat: support withComponent on all react-css components
Browse files Browse the repository at this point in the history
This adds full compatibility support between the emotion and css versions of tablekit so we can start phasing out the emotion version.
  • Loading branch information
SimeonC committed Nov 26, 2024
1 parent 7f5d768 commit cf6aa43
Show file tree
Hide file tree
Showing 87 changed files with 19,470 additions and 19,781 deletions.
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v21.6.2
v22.11.0
2 changes: 1 addition & 1 deletion .tool-versions
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
nodejs 21.6.2
nodejs 22.11.0
bun 1.1.33
37,746 changes: 18,525 additions & 19,221 deletions package-lock.json

Large diffs are not rendered by default.

186 changes: 128 additions & 58 deletions system/core/scripts/generateComponents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,17 @@ function capitalize(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}

interface PropsType {
key: string;
value: string;
type: 'value' | 'string';
}

abstract class ComponentBuilder {
static headerComment = `/**
* DO NOT EDIT: This file is generated in the post-build step of @tablecheck/tablekit-core
* If you need to provide more "structure" to this component move it to the 'structuredComponents' folder
*/`;
* DO NOT EDIT: This file is generated in the post-build step of @tablecheck/tablekit-core
* If you need to provide more "structure" to this component move it to the 'structuredComponents' folder
*/`;

protected importKey: string;

Expand All @@ -29,7 +35,7 @@ abstract class ComponentBuilder {

protected structuredFileNames: string[];

protected fixedProps: string[];
protected fixedProps: PropsType[];

protected defaultProps: [string, string][];

Expand Down Expand Up @@ -82,6 +88,7 @@ abstract class ComponentBuilder {
this.fixedProps = [];
this.importKey = importedKey;
this.element = importedValues.element || 'div';
this.variantStyles = importedValues.variantStyles;
const defaultProps = importedValues.defaultProps
? { ...importedValues.defaultProps }
: {};
Expand Down Expand Up @@ -130,10 +137,13 @@ abstract class ComponentBuilder {
}

hasVariants() {
return (
typeof this.variantStyles === 'object' &&
Object.keys(this.variantStyles).length
);
return !!this.getVariants().length;
}

getVariants() {
return typeof this.variantStyles === 'object'
? Object.keys(this.variantStyles)
: [];
}

// eslint-disable-next-line class-methods-use-this
Expand All @@ -150,70 +160,91 @@ abstract class ComponentBuilder {
} & ${this.reactHtmlAttributesType};`;
}

writeForwardRefComponent({
abstract writeForwardRefComponent({
varName,
elementName,
omitVariants,
displayName,
fixedProps = this.fixedProps || []
}) {
return `export const ${varName} = ${this.writeForwardRefComponentFunction({
elementName,
omitVariants,
fixedProps
})};
${varName}.displayName = \`${displayName || varName}\`;`;
}
}): string;

writeForwardRefComponentFunction({
elementName,
omitVariants,
fixedProps: fixedPropsArg = this.fixedProps || []
}) {
const fixedProps = [...fixedPropsArg];
const props = this.buildProps(fixedPropsArg)
.map(
({ key, value, type }) =>
`${key}=${type === 'string' ? `"${value}"` : `{${value}}`}`
)
.join(' ');
const propsType = omitVariants ? 'Omit<Props, "data-variant">' : 'Props';
return `React.forwardRef<${this.elementType}, ${propsType} & ${this.reactHtmlAttributesType}>((props, ref) => <${elementName} {...props} ${props} ref={ref} />)`;
}

protected buildProps(fixedProps: PropsType[]): PropsType[] {
const props = [...fixedProps];
this.defaultProps.forEach(([key]) => {
fixedProps.push(
`${key}={props${this.formatKey(key)} ?? (${
this.importKey
}.defaultProps${this.formatKey(key)} as never)}`
);
props.push({
key,
type: 'value',
value: this.formatDefaultProp(key)
});
});
if (
this.element === 'button' &&
!this.defaultProps.find(([key]) => key === 'type')
) {
fixedProps.push(`type="button" `);
props.push({ key: 'type', type: 'string', value: 'button' });
}
this.configurableProps.forEach(([propKey, configKey]) => {
fixedProps.push(
`${propKey}={props${this.formatKey(
propKey
)} ?? getConfigDefault('${configKey}')}`
);
props.push({
key: propKey,
type: 'value',
value: this.formatConfigurableProp(propKey, configKey)
});
});
const propsType = omitVariants ? 'Omit<Props, "data-variant">' : 'Props';
return `React.forwardRef<${this.elementType}, ${propsType} & ${
this.reactHtmlAttributesType
}>((props, ref) => <${elementName} {...props} ${fixedProps.join(
' '
)} ref={ref} />)`;
return props;
}

protected formatDefaultProp(key: string) {
return `props${this.formatKey(key)} ?? (${
this.importKey
}.defaultProps${this.formatKey(key)} as never)`;
}

protected formatConfigurableProp(key, configKey) {
return `props${this.formatKey(key)} ?? getConfigDefault('${configKey}')`;
}
}

class CssComponentBuilder extends ComponentBuilder {
private className: string;

private selectors: string[];
get localImports() {
return [
...super.localImports,
this.hasVariants()
? `import { buildVariantComponents, buildWithComponent } from '../utils';`
: `import { buildWithComponent } from '../utils';`
];
}

constructor(importedKey, importedValues) {
super(cssOutputFolderPath, importedKey, importedValues);
this.className =
this.pascalImportKey === 'InputLikeButton'
? 'input'
: importedValues.className || '';
this.selectors = importedValues.selectors;
this.fixedProps = this.className
? [`className={\`\${(props.className ?? '')} ${this.className}\`}`]
? [
{
key: 'className',
type: 'string',
value: this.className
}
]
: [];
}

Expand Down Expand Up @@ -247,35 +278,75 @@ class CssComponentBuilder extends ComponentBuilder {
this.writeForwardRefComponent({
varName: this.pascalImportKey,
elementName: this.element,
omitVariants: false,
displayName: this.pascalImportKey
displayName: this.pascalImportKey,
shouldExport: true
})
);

if (this.hasVariants()) {
fileContent.push(
`export const Variant${this.pascalImportKey} = Object.entries(${
this.importKey
}.variantStyles).reduce(
(result, [key, value]) => {
result[\`\${key.charAt(0).toUpperCase()}\${key.slice(1).toLowerCase()}\`] = ${this.writeForwardRefComponentFunction(
{
elementName: this.element,
omitVariants: true,
fixedProps: [...this.fixedProps, `data-variant={key}`]
}
)};
return result;
},
{} as Record<Capitalize<${
this.pascalImportKey
}Variant>, React.ComponentType<${this.getBasePropsType()}>>
);`
`export const Variant${this.pascalImportKey} = buildVariantComponents<
${this.getVariants()
.map((v) => `'${v}'`)
.join(' | ')},
Props,
'${this.element}'
>({
variants: ${JSON.stringify(this.getVariants())},
className: '${this.className}',
element: '${this.element}',
displayName: '${this.pascalImportKey}'
});`
);
}

return fileContent;
}

writeForwardRefComponent({
varName,
elementName,
omitVariants = false,
displayName = '',
fixedProps: fixedPropsArg = this.fixedProps || [],
shouldExport = false
}) {
const propsType = omitVariants ? 'Omit<Props, "data-variant">' : 'Props';
const props = this.buildProps(fixedPropsArg);
const classNameProp = props.find(({ key }) => key === 'className');
const append: string[] = [
`className: ${classNameProp ? `'${classNameProp.value}'` : 'undefined'}`
];
const additionalProps = props.filter(({ key }) => key !== 'className');
if (additionalProps.length) {
append.push(
`additionalProps: { ${additionalProps
.map(
({ key, value, type }) =>
`'${key}': ${type === 'string' ? `'${value}'` : value}`
)
.join(', ')} }`
);
}
return `${
shouldExport ? 'export ' : ''
}const ${varName} = buildWithComponent<${
this.elementType
}, ${propsType} & ${
this.reactHtmlAttributesType
}>({ tag: '${elementName}', displayName: '${
displayName || varName
}', ${append.join(', ')}});`;
}

protected formatDefaultProp(key: string) {
return `(${this.importKey}.defaultProps${this.formatKey(key)} as never)`;
}

// eslint-disable-next-line class-methods-use-this
protected formatConfigurableProp(key, configKey) {
return `{ toString: () => getConfigDefault('${configKey}') }`;
}
}

class ReactComponentBuilder extends ComponentBuilder {
Expand Down Expand Up @@ -304,7 +375,6 @@ class ReactComponentBuilder extends ComponentBuilder {

constructor(importedKey, importedValues) {
super(reactOutputFolderPath, importedKey, importedValues);
this.variantStyles = importedValues.variantStyles;
this.fullStyles = importedValues.fullStyles;
this.coreStyles = importedValues.coreStyles;
}
Expand Down
1 change: 0 additions & 1 deletion system/core/src/components/InputAlert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ export const fullStyles = css`
display: grid;
grid-template-columns: min-content 1fr;
gap: var(--spacing-l2);
margin-top: var(--spacing-l2);
color: var(--text);
border-radius: var(--border-radius-small);
Expand Down
1 change: 1 addition & 0 deletions system/core/src/components/InputStructure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ export const fullStyles = css`
.label-row {
display: flex;
justify-content: space-between;
align-items: baseline;
}
`;
6 changes: 3 additions & 3 deletions system/css/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@
"@tablecheck/tablekit-core": "^4.0.0",
"@types/fs-extra": "11.0.1",
"chalk": "5.2.0",
"cssnano": "5.1.15",
"cssnano": "7.0.6",
"execa": "6.1.0",
"fs-extra": "11.1.0",
"lodash": "4.17.21",
"pluralize": "8.0.0",
"postcss": "8.4.38",
"postcss-nesting": "12.1.5",
"postcss": "8.4.49",
"postcss-nesting": "13.0.1",
"typescript": "4.9.5"
},
"publishConfig": {
Expand Down
1 change: 1 addition & 0 deletions system/react-css/scripts/generateIndex.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ Promise.all(filenames.map(getExport)).then((exportLines) => {
* The exports here are generated from all ts/tsx files at the root level
*/
export * from './config';
export type { WithComponentType } from './utils';
${fileContent}`,
{ filepath, ...config }
)
Expand Down
26 changes: 0 additions & 26 deletions system/react-css/src/buildVariantComponents.ts

This file was deleted.

9 changes: 4 additions & 5 deletions system/react-css/src/components/Anchor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,12 @@
import { anchor } from '@tablecheck/tablekit-core';
import * as React from 'react';

import { buildWithComponent } from '../utils';

export type Props = anchor.Props &
React.AnchorHTMLAttributes<HTMLAnchorElement>;

export const Anchor = React.forwardRef<
export const Anchor = buildWithComponent<
HTMLAnchorElement,
Props & React.AnchorHTMLAttributes<HTMLAnchorElement>
>((props, ref) => (
<a {...props} className={`${props.className ?? ''} link`} ref={ref} />
));
Anchor.displayName = `Anchor`;
>({ tag: 'a', displayName: 'Anchor', className: 'link' });
Loading

0 comments on commit cf6aa43

Please sign in to comment.