Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: correctly scope checkboxes and radios in classy to avoid conflicts #246

Merged
merged 1 commit into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 61 additions & 43 deletions system/core/scripts/generateComponents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ abstract class ComponentBuilder {

protected importKey: string;

protected element: string;
protected element: string | undefined;

protected filePath: string;

Expand Down Expand Up @@ -70,24 +70,28 @@ abstract class ComponentBuilder {
return `HTML${this.reactElementType}Element`;
}

get safeElement() {
return this.element || 'div';
}

get reactHtmlAttributesType() {
if (['span', 'div'].includes(this.element))
if (['span', 'div'].includes(this.safeElement))
return `React.HTMLAttributes<${this.elementType}>`;
return `React.${
this.reactElementType === 'TextArea' ? 'Textarea' : this.reactElementType
}HTMLAttributes<${this.elementType}>`;
}

get reactElementType() {
if (this.element === 'a') return 'Anchor';
if (this.element === 'textarea') return 'TextArea';
return capitalize(this.element);
if (this.safeElement === 'a') return 'Anchor';
if (this.safeElement === 'textarea') return 'TextArea';
return capitalize(this.safeElement);
}

constructor(outputFolderPath, importedKey, importedValues) {
this.fixedProps = [];
this.importKey = importedKey;
this.element = importedValues.element || 'div';
this.element = importedValues.element;
this.variantStyles = importedValues.variantStyles;
const defaultProps = importedValues.defaultProps
? { ...importedValues.defaultProps }
Expand Down Expand Up @@ -165,7 +169,7 @@ abstract class ComponentBuilder {
elementName,
omitVariants,
displayName,
fixedProps = this.fixedProps || []
fixedProps
}): string;

writeForwardRefComponentFunction({
Expand Down Expand Up @@ -193,7 +197,7 @@ abstract class ComponentBuilder {
});
});
if (
this.element === 'button' &&
this.safeElement === 'button' &&
!this.defaultProps.find(([key]) => key === 'type')
) {
props.push({ key: 'type', type: 'string', value: 'button' });
Expand Down Expand Up @@ -233,10 +237,7 @@ class CssComponentBuilder extends ComponentBuilder {

constructor(importedKey, importedValues) {
super(cssOutputFolderPath, importedKey, importedValues);
this.className =
this.pascalImportKey === 'InputLikeButton'
? 'input'
: importedValues.className || '';
this.className = importedValues.className || '';
this.fixedProps = this.className
? [
{
Expand All @@ -249,20 +250,22 @@ class CssComponentBuilder extends ComponentBuilder {
}

isValidComponentImport() {
if (!this.hasValidSelectors()) {
const isValid = this.hasValidSelectors();
if (!isValid) {
throw new Error(`${this.importKey} does not have valid selectors`);
}
if (isValid === 'skip') {
console.warn(
`Skipping ${this.importKey} as it does not have valid selectors`
`Skipping ${this.importKey} as it does not export 'element' or className`
);
return false;
}
return super.isValidComponentImport();
}

hasValidSelectors() {
if (this.pascalImportKey === 'Spinner') return true;
if (this.className) return true;
if (!this.element) return false;
return !!this.defaultProps.find(([key]) => key === 'type');
if (!this.element && !this.className) return 'skip';
return !!this.className;
}

buildFileContent() {
Expand All @@ -277,7 +280,7 @@ class CssComponentBuilder extends ComponentBuilder {
fileContent.push(
this.writeForwardRefComponent({
varName: this.pascalImportKey,
elementName: this.element,
elementName: this.safeElement,
displayName: this.pascalImportKey,
shouldExport: true
})
Expand All @@ -290,12 +293,13 @@ class CssComponentBuilder extends ComponentBuilder {
.map((v) => `'${v}'`)
.join(' | ')},
Props,
'${this.element}'
'${this.safeElement}'
>({
variants: ${JSON.stringify(this.getVariants())},
className: '${this.className}',
element: '${this.element}',
displayName: '${this.pascalImportKey}'
${this.buildWithComponentProps({
elementName: this.safeElement,
displayName: this.pascalImportKey
})},
});`
);
}
Expand All @@ -306,17 +310,37 @@ class CssComponentBuilder extends ComponentBuilder {
writeForwardRefComponent({
varName,
elementName,
omitVariants = false,
displayName = '',
omitVariants = false,
fixedProps: fixedPropsArg = this.fixedProps || [],
shouldExport = false
}) {
const propsType = omitVariants ? 'Omit<Props, "data-variant">' : 'Props';
const propsType = `${
omitVariants ? 'Omit<Props, "data-variant">' : 'Props'
} & ${this.reactHtmlAttributesType}`;
return `${
shouldExport ? 'export ' : ''
}const ${varName} = buildWithComponent<${
this.elementType
}, ${propsType}>({ ${this.buildWithComponentProps({
elementName,
displayName: displayName || varName,
fixedProps: fixedPropsArg
})}});`;
}

// eslint-disable-next-line @typescript-eslint/naming-convention
private buildWithComponentProps({
elementName,
displayName = '',
fixedProps: fixedPropsArg = this.fixedProps || []
}) {
const props = this.buildProps(fixedPropsArg);
const classNameProp = props.find(({ key }) => key === 'className');
const append: string[] = [
`className: ${classNameProp ? `'${classNameProp.value}'` : 'undefined'}`
];
if (!classNameProp) {
throw new Error(`Missing className prop for ${displayName}`);
}
const append: string[] = [`className: '${classNameProp.value}'`];
const additionalProps = props.filter(({ key }) => key !== 'className');
if (additionalProps.length) {
append.push(
Expand All @@ -328,15 +352,9 @@ class CssComponentBuilder extends ComponentBuilder {
.join(', ')} }`
);
}
return `${
shouldExport ? 'export ' : ''
}const ${varName} = buildWithComponent<${
this.elementType
}, ${propsType} & ${
this.reactHtmlAttributesType
}>({ tag: '${elementName}', displayName: '${
displayName || varName
}', ${append.join(', ')}});`;
return `tag: '${elementName}', displayName: '${displayName}', ${append.join(
', '
)}`;
}

protected formatDefaultProp(key: string) {
Expand Down Expand Up @@ -399,15 +417,15 @@ class ReactComponentBuilder extends ComponentBuilder {
}
fileContent.push('');
fileContent.push(
`const Base = styled.${this.element}<${this.getBasePropsType()}>\`\${${
this.importKey
}.fullStyles}\`;`
`const Base = styled.${
this.safeElement
}<${this.getBasePropsType()}>\`\${${this.importKey}.fullStyles}\`;`
);
if (this.coreStyles) {
fileContent.push(
`const Core = styled.${this.element}<${this.getBasePropsType()}>\`\${${
this.importKey
}.coreStyles}\`;`
`const Core = styled.${
this.safeElement
}<${this.getBasePropsType()}>\`\${${this.importKey}.coreStyles}\`;`
);
}

Expand Down
1 change: 1 addition & 0 deletions system/core/src/components/Checkbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { OptionalKeys, css } from '../utils';

export const element = 'input';
export const selectors = ['input[type="checkbox"]:not(.toggle)'];
export const className = 'checkbox';

export interface Props {
type: 'checkbox';
Expand Down
2 changes: 2 additions & 0 deletions system/core/src/components/CheckboxRadioLabel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { css } from '../utils';

export const element = 'label';
export const selectors = ['label.checkbox', 'label.radio'];
export const className = 'radio';
export const classNameSelector = 'label.checkbox, label.radio';

export interface Props {
htmlFor?: string;
Expand Down
1 change: 1 addition & 0 deletions system/core/src/components/InputLikeButton.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export type {

export const element = 'button';
export const selectors = ['button.input', 'a.input'];
export const className = 'input-like-button';

export const defaultProps = {
role: 'button'
Expand Down
4 changes: 0 additions & 4 deletions system/core/src/components/InputWithIcons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,6 @@ export const fullStyles = css`
}
& > [data-mode='input-append'] {
height: calc(var(--tk-input-height) - var(--tk-input-border-width) * 2);
width: calc(
var(--tk-input-icon-size)+ var(--tk-input-icon-gap)+
var(--tk-input-icon-end-padding)
);
--tk-icon-button-padding: 8px !important;
margin-top: 0;
margin-bottom: 0;
Expand Down
4 changes: 0 additions & 4 deletions system/core/src/components/InputWithPrefix.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,6 @@ export const fullStyles = css`
& > [data-mode='input-append'] {
grid-area: 1/4/1/5;
height: calc(var(--tk-input-height) - var(--tk-input-border-width) * 2);
width: calc(
var(--tk-input-icon-size)+ var(--tk-input-icon-gap)+
var(--tk-input-icon-end-padding)
);
--tk-icon-button-padding: 8px !important;
margin-top: 0;
margin-bottom: 0;
Expand Down
1 change: 1 addition & 0 deletions system/core/src/components/Radio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { OptionalKeys, css } from '../utils';

export const element = 'input';
export const selectors = ['input[type="radio"]:not(.toggle)'];
export const className = 'radio';

export interface Props {
type: 'radio';
Expand Down
1 change: 1 addition & 0 deletions system/core/src/components/Spinner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export const element = 'span';
export const selectors = [
'[aria-busy="true"]:not(button):not(select):not(input):not(textarea)'
];
export const className = 'spinner';

export interface Props {
'aria-busy': boolean;
Expand Down
6 changes: 6 additions & 0 deletions system/core/src/components/Table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,16 @@ export const fullStyles = css`

& tr:hover {
--tk-table-row-background: var(--surface-raised-hover);
[data-variant='bare']:hover {
--tk-button-background-color: var(--surface-hover-transparent);
}
}

& tr[data-active='true'] {
--tk-table-row-background: var(--surface-raised-active);
[data-variant='bare']:hover {
--tk-button-background-color: var(--surface-hover-transparent);
}
}
}

Expand Down
4 changes: 0 additions & 4 deletions system/core/src/components/TextAreaWithIcons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,6 @@ export const fullStyles = css`
}
& > [data-mode='input-append'] {
height: calc(var(--tk-input-height) - var(--tk-input-border-width) * 2);
width: calc(
var(--tk-input-icon-size)+ var(--tk-input-icon-gap)+
var(--tk-input-icon-end-padding)
);
--tk-icon-button-padding: 8px !important;
margin-top: 0;
margin-bottom: 0;
Expand Down
4 changes: 0 additions & 4 deletions system/core/src/components/TextAreaWithPrefix.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,6 @@ export const fullStyles = css`
& > [data-mode='input-append'] {
grid-area: 1/4/1/5;
height: calc(var(--tk-input-height) - var(--tk-input-border-width) * 2);
width: calc(
var(--tk-input-icon-size)+ var(--tk-input-icon-gap)+
var(--tk-input-icon-end-padding)
);
--tk-icon-button-padding: 8px !important;
margin-top: 0;
margin-bottom: 0;
Expand Down
1 change: 1 addition & 0 deletions system/core/src/components/Tooltip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { css } from '../utils';

export const element = 'span';
export const selectors = ['[data-tooltip]'];
export const className = 'tooltip';

export interface Props {
'data-tooltip': string;
Expand Down
16 changes: 14 additions & 2 deletions system/css/src/buildFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ export class ThemeVariablesBuilder extends CssFileBuilder {
interface ComponentFileContent {
element?: string;
className?: string;
classNameSelector?: string;
selectors?: string[];
fullStyles: string;
variantStyles?: Record<string, string>;
Expand All @@ -139,8 +140,19 @@ export class ComponentBuilder extends CssFileBuilder<ComponentFileContent> {
}

getClassySelectors() {
const { element, className, selectors = [] } = this.fileContent;
if (!className) return selectors;
const {
element,
className,
classNameSelector,
selectors = []
} = this.fileContent;
if (!className) {
throw new Error(
`className is missing in ${this.folderName}/${this.fileName}`
);
return selectors;
}
if (classNameSelector) return [classNameSelector];
if (element === 'input') return [`input.${className}`];
return [`.${className}`];
}
Expand Down
6 changes: 3 additions & 3 deletions system/react-css/src/components/Badge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export const VariantBadge = buildVariantComponents<
'orange',
'disabled'
],
className: 'badge',
element: 'span',
displayName: 'Badge'
tag: 'span',
displayName: 'Badge',
className: 'badge'
});
6 changes: 3 additions & 3 deletions system/react-css/src/components/Banner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const VariantBanner = buildVariantComponents<
'div'
>({
variants: ['tertiary', 'ghost', 'success', 'warning', 'info', 'neutral'],
className: 'banner',
element: 'div',
displayName: 'Banner'
tag: 'div',
displayName: 'Banner',
className: 'banner'
});
8 changes: 6 additions & 2 deletions system/react-css/src/components/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,11 @@ export const VariantButton = buildVariantComponents<
'bare',
'danger'
],
tag: 'button',
displayName: 'Button',
className: 'btn',
element: 'button',
displayName: 'Button'
additionalProps: {
type: button.defaultProps.type as never,
'data-size': { toString: () => getConfigDefault('controlSize') }
}
});
2 changes: 1 addition & 1 deletion system/react-css/src/components/Checkbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ export const Checkbox = buildWithComponent<
>({
tag: 'input',
displayName: 'Checkbox',
className: undefined,
className: 'checkbox',
additionalProps: { type: checkbox.defaultProps.type as never }
});
Loading
Loading