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

Feat/generator api update #13

Merged
merged 15 commits into from
Nov 23, 2020
5 changes: 2 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
language: node_js
node_js:
- 12
- 14.5.0

before_script:
- yarn build

script:
- yarn bundlesize
- yarn lint
- yarn test
- yarn test -u

after_success: yarn coverage
2 changes: 1 addition & 1 deletion packages/block/src/BlockBase.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import { createStyledComponent } from '@artifak/component-generator';

export const BlockBase = createStyledComponent();
export const BlockBase = createStyledComponent({ element: 'div' });
6 changes: 3 additions & 3 deletions packages/block/src/createBlockComponents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { BlockBaseProps } from './typings';
import { BlockBase } from './BlockBase';

/* eslint-disable @typescript-eslint/no-explicit-any */
export function createBlockComponents<ST>(
styles: ST
export function createBlockComponents<S>(
styles: S
): { [key in keyof any]: React.FC<BlockBaseProps> } {
return createBaseComponents<ST, BlockBaseProps>(BlockBase, styles);
return createBaseComponents<S, BlockBaseProps>(BlockBase, styles);
}
100 changes: 100 additions & 0 deletions packages/component-generator/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# `@artifak/component-generator`

Component Generator library to generate similar components based off of styles and or attributes you provide. You can also view the docs
at [Artifak Typography](https://www.artifak.dev/?content=Generator).

## Installation

### Yarn

```sh
yarn add @artifak/component-generator
```

### NPM

```sh
npm install @artifak/component-generator
```

## Usage

### createStyledComponent

To generate your components, simply define a config object consisting of styles/attributes with the key as your component name. Note that you **must** define the `as` property in order to have it render as the HTML tag that you desire. In the example below, we've defined a component called `H1` to render as an `h1` HTML tag. We've also defined the fontSize in an array that matches the media query widths.

| Arguments | Type |
| --------- | ------ |
| config | object |

```ts
import { createStyledComponent } from '@artifak/component-generator';
import { system } from 'styled-system';

const styles = {
fontSize: [16, 36, 96],
margin: 0,
as: 'h1'
};

const variants = {
primary: {
color: 'red'
},
secondary: {
color: 'blue'
}
};

const styleProps = [
system({
textTransform: true
})
];

const element = 'h1';

const H1 = createStyledComponent<typeof config>({
styles,
variants,
styleProps,
element
});
```

### createBaseComponents

createBaseComponents will require a base Styled Component to generate your desired components. This is used to conveniently generate similar components. For creating the base component, you can use createStyledComponent.

| Arguments | Type |
| --------- | ------ |
| styles | object |

```ts
import {
createStyledComponent,
createBaseComponents
} from '@artifak/component-generator';

const baseComponentConfig = {
styles: {
display: 'block',
margin: '0 auto'
}
};

const BaseComponent = createStyledComponent<typeof baseComponentConfig>();

const typeSettings = {
H1: {
styles: {
fontSize: [16, 17, 19, 21],
lineHeight: 1.5,
marginTop: 0,
as: 'h1'
}
}
};

const { H1 } = createBaseComponents<typeof typeSettings>(typeSettings);
```
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,17 @@ import {

export const mockComponentStyles = {
H1: {
marginBottom: ['15px'],
as: 'h1'
styles: {
marginBottom: ['15px'],
as: 'h1'
}
},

H2: {
marginTop: 0,
as: 'h2'
styles: {
marginTop: 0,
as: 'h2'
}
}
};

Expand Down
15 changes: 12 additions & 3 deletions packages/component-generator/__tests__/component-generator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,20 @@ describe('@artifak/component-generator', () => {
describe('createStyledFunctionComponent', () => {
it('should return a component', () => {
expect(
createStyledFunctionComponent(MockComponent, mockComponentStyles)
createStyledFunctionComponent(
MockComponent,
mockComponentStyles.H1.styles
)
).toBeDefined();
});
});

describe('createStyledComponent', () => {
it('should return a styled component with default config when no arguments are provided', () => {
const resultingStyledComponent = createStyledComponent({});
expect(resultingStyledComponent).toBeDefined();
});

it('should return a styled component', () => {
const mockConfig = system({
textDecoration: true,
Expand All @@ -52,8 +60,9 @@ describe('@artifak/component-generator', () => {
wordSpacing: true
});

const resultingStyledComponent = createStyledComponent([mockConfig], {
fontSize: '1em'
const resultingStyledComponent = createStyledComponent({
styles: { fontSize: '1em' },
styleProps: [mockConfig]
});

expect(resultingStyledComponent).toBeDefined();
Expand Down
109 changes: 109 additions & 0 deletions packages/component-generator/index.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import React from 'react';
import styled, { css } from 'styled-components';
import { variant, system } from 'styled-system';
import { createStyledComponent, createBaseComponents } from './src';

const StyledH1 = createStyledComponent({
styles: {
fontSize: '48px',
margin: '0 0 0.25em',
lineHeight: 1.5
},
variants: {
primary: {
color: 'red'
},
secondary: {
color: 'hotpink'
}
},
element: 'h1'
});

export function StyledH1Componennt() {
return (
<>
<StyledH1>Hello World</StyledH1>
<StyledH1 variant="primary">Hello World</StyledH1>
<StyledH1 variant="secondary">Hello World</StyledH1>
</>
);
}

const typoConfig = {
H1: {
styles: {
fontSize: [48, 96],
margin: '0 0 0.25em',
lineHeight: 1.5,
as: 'h1'
}
},

H2: {
styles: {
fontSize: [37, 39, 41, 43],
lineHeight: 1.5,
marginTop: 0,
as: 'h2'
},
attrs: {
tabIndex: 1
}
},

H3: {
styles: {
fontSize: [27, 28, 30, 32],
lineHeight: 1.5,
as: 'h3'
}
}
};

const typographyStyleProps = system({
textDecoration: true,
textIndent: true,
textTransform: true,
textOverflow: true,
whiteSpace: true,
wordBreak: true,
wordSpacing: true
});

const TypographyBase = createStyledComponent<typeof typographyStyleProps>({
styleProps: [typographyStyleProps]
});

const { H1 } = createBaseComponents<typeof typoConfig, any, HTMLHeadElement>(
TypographyBase,
typoConfig
);

const VariantH1 = styled(H1)(
variant({
variants: {
primary: {
color: 'red'
},
secondary: {
color: 'hotpink'
}
}
})
);

export function GenVariadicH1() {
return (
<>
<VariantH1>Hello World</VariantH1>
<VariantH1 variant="primary">Hello World</VariantH1>
<VariantH1 variant="secondary">Hello World</VariantH1>
</>
);
}

export default {
title: 'Generator',
component: [StyledH1Componennt]
};
42 changes: 26 additions & 16 deletions packages/component-generator/src/createBaseComponents.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,44 @@
import React from 'react';
import React, { HTMLAttributes } from 'react';
import { AnyStyledComponent } from 'styled-components';
import { ComponentVariant } from './typings';

export function createBaseComponents<ST, CP>(
export type Settings<A> = {
/* eslint-disable @typescript-eslint/no-explicit-any */
styles: { [key in keyof any]: string | string[] | number | number[] };
attrs?: A;
};

export function createBaseComponents<S, P, A>(
BaseStyledComponent: AnyStyledComponent,
styles: ST
): { [key in keyof ST]: React.FC<CP & ComponentVariant> } {
return !!styles && Object.keys(styles).length >= 1
? Object.entries(styles).reduce((acc, entry) => {
const [prop, style] = entry;
settings: { [key in keyof S]: Settings<HTMLAttributes<A>> }
): { [key in keyof S]: React.FC<P & ComponentVariant> } {
return !!settings && Object.keys(settings).length >= 1
? Object.entries(settings).reduce((acc, entry) => {
const [prop, setting] = entry;
const { styles, attrs } = setting as Settings<HTMLAttributes<A>>;

if (hasKey(styles, prop)) {
acc[prop] = createStyledFunctionComponent<ST, CP>(
if (hasKey(settings, prop)) {
acc[prop] = createStyledFunctionComponent<A, P>(
BaseStyledComponent,
style
styles,
attrs
);

acc[prop].displayName = prop;
}

return acc;
}, {} as { [key in keyof ST]: React.FC<CP & ComponentVariant> })
: ({} as { [key in keyof ST]: React.FC<CP & ComponentVariant> });
}, {} as { [key in keyof S]: React.FC<P & ComponentVariant> })
: ({} as { [key in keyof S]: React.FC<P & ComponentVariant> });
}

export function createStyledFunctionComponent<ST, CP>(
export function createStyledFunctionComponent<A, P>(
BaseStyledComponent: AnyStyledComponent,
styles: ST
): React.FC<CP> {
styles: { [key: string]: string | string[] | number | number[] },
attrs: HTMLAttributes<A> = {}
): React.FC<P> {
return props => (
<BaseStyledComponent {...styles} {...props}>
<BaseStyledComponent {...styles} {...attrs} {...props}>
{props.children}
</BaseStyledComponent>
);
Expand Down
Loading