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

CLI: Update renderer templates to provide correct typescript examples #21647

Merged
merged 2 commits into from
Mar 20, 2023
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
9 changes: 8 additions & 1 deletion code/lib/cli/src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,9 @@ export async function copyTemplateFiles({
destination,
includeCommonAssets = true,
}: CopyTemplateFilesOptions) {
const languageFolderMapping: Record<SupportedLanguage, string> = {
const languageFolderMapping: Record<SupportedLanguage | 'typescript', string> = {
// keeping this for backwards compatibility in case community packages are using it
typescript: 'ts',
[SupportedLanguage.JAVASCRIPT]: 'js',
[SupportedLanguage.TYPESCRIPT_3_8]: 'ts-3-8',
[SupportedLanguage.TYPESCRIPT_4_9]: 'ts-4-9',
Expand All @@ -212,6 +214,7 @@ export async function copyTemplateFiles({

const assetsLanguage = join(assetsDir, languageFolderMapping[language]);
const assetsJS = join(assetsDir, languageFolderMapping[SupportedLanguage.JAVASCRIPT]);
const assetsTS = join(assetsDir, languageFolderMapping.typescript);
const assetsTS38 = join(assetsDir, languageFolderMapping[SupportedLanguage.TYPESCRIPT_3_8]);

// Ideally use the assets that match the language & version.
Expand All @@ -222,6 +225,10 @@ export async function copyTemplateFiles({
if (language === SupportedLanguage.TYPESCRIPT_4_9 && (await fse.pathExists(assetsTS38))) {
return assetsTS38;
}
// Fallback further to TS (for backwards compatibility purposes)
if (await fse.pathExists(assetsTS)) {
return assetsTS;
}
// Fallback further to JS
if (await fse.pathExists(assetsJS)) {
return assetsJS;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ type Story = StoryObj<HeaderProps>;
export const LoggedIn: Story = {
args: {
user: {
name: 'John Doe',
name: 'Jane Doe',
},
},
};
Expand Down
55 changes: 55 additions & 0 deletions code/renderers/html/template/cli/ts-4-9/Button.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import type { StoryObj, Meta } from '@storybook/html';
import type { ButtonProps } from './Button';
import { createButton } from './Button';

// More on how to set up stories at: https://storybook.js.org/docs/html/writing-stories/introduction#default-export
const meta = {
title: 'Example/Button',
tags: ['autodocs'],
render: (args) => {
// You can either use a function to create DOM elements or use a plain html string!
// return `<div>${label}</div>`;
return createButton(args);
},
argTypes: {
backgroundColor: { control: 'color' },
label: { control: 'text' },
onClick: { action: 'onClick' },
primary: { control: 'boolean' },
size: {
control: { type: 'select' },
options: ['small', 'medium', 'large'],
},
},
} satisfies Meta<ButtonProps>;

export default meta;
type Story = StoryObj<ButtonProps>;

// More on writing stories with args: https://storybook.js.org/docs/7.0/html/writing-stories/args
export const Primary: Story = {
args: {
primary: true,
label: 'Button',
},
};

export const Secondary: Story = {
args: {
label: 'Button',
},
};

export const Large: Story = {
args: {
size: 'large',
label: 'Button',
},
};

export const Small: Story = {
args: {
size: 'small',
label: 'Button',
},
};
51 changes: 51 additions & 0 deletions code/renderers/html/template/cli/ts-4-9/Button.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import './button.css';

export interface ButtonProps {
/**
* Is this the principal call to action on the page?
*/
primary?: boolean;
/**
* What background color to use
*/
backgroundColor?: string;
/**
* How large should the button be?
*/
size?: 'small' | 'medium' | 'large';
/**
* Button contents
*/
label: string;
/**
* Optional click handler
*/
onClick?: () => void;
}

/**
* Primary UI component for user interaction
*/
export const createButton = ({
primary = false,
size = 'medium',
backgroundColor,
label,
onClick,
}: ButtonProps) => {
const btn = document.createElement('button');
btn.type = 'button';
btn.innerText = label;
if (onClick) {
btn.addEventListener('click', onClick);
}

const mode = primary ? 'storybook-button--primary' : 'storybook-button--secondary';
btn.className = ['storybook-button', `storybook-button--${size}`, mode].join(' ');

if (backgroundColor) {
btn.style.backgroundColor = backgroundColor;
}

return btn;
};
33 changes: 33 additions & 0 deletions code/renderers/html/template/cli/ts-4-9/Header.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import type { Meta, StoryObj } from '@storybook/html';
import type { HeaderProps } from './Header';
import { createHeader } from './Header';

const meta = {
title: 'Example/Header',
// This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/7.0/html/writing-docs/docs-page
tags: ['autodocs'],
render: (args) => createHeader(args),
parameters: {
// More on how to position stories at: https://storybook.js.org/docs/7.0/html/configure/story-layout
layout: 'fullscreen',
},
// More on argTypes: https://storybook.js.org/docs/7.0/html/api/argtypes
argTypes: {
onLogin: { action: 'onLogin' },
onLogout: { action: 'onLogout' },
onCreateAccount: { action: 'onCreateAccount' },
},
} satisfies Meta<HeaderProps>;

export default meta;
type Story = StoryObj<HeaderProps>;

export const LoggedIn: Story = {
args: {
user: {
name: 'Jane Doe',
},
},
};

export const LoggedOut: Story = {};
54 changes: 54 additions & 0 deletions code/renderers/html/template/cli/ts-4-9/Header.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import './header.css';
import { createButton } from './Button';

export interface HeaderProps {
user?: { name: string };
onLogin: () => void;
onLogout: () => void;
onCreateAccount: () => void;
}

export const createHeader = ({ user, onLogout, onLogin, onCreateAccount }: HeaderProps) => {
const header = document.createElement('header');

const wrapper = document.createElement('div');
wrapper.className = 'wrapper';

const logo = `<div>
<svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
<g fill="none" fillRule="evenodd">
<path
d="M10 0h12a10 10 0 0110 10v12a10 10 0 01-10 10H10A10 10 0 010 22V10A10 10 0 0110 0z"
fill="#FFF" />
<path
d="M5.3 10.6l10.4 6v11.1l-10.4-6v-11zm11.4-6.2l9.7 5.5-9.7 5.6V4.4z"
fill="#555AB9" />
<path d="M27.2 10.6v11.2l-10.5 6V16.5l10.5-6zM15.7 4.4v11L6 10l9.7-5.5z" fill="#91BAF8" />
</g>
</svg>
<h1>Acme</h1>
</div>`;

wrapper.insertAdjacentHTML('afterbegin', logo);

const account = document.createElement('div');
if (user) {
const welcomeMessage = `<span class="welcome">Welcome, <b>${user.name}</b>!</span>`;
account.innerHTML = welcomeMessage;
account.appendChild(createButton({ size: 'small', label: 'Log out', onClick: onLogout }));
} else {
account.appendChild(createButton({ size: 'small', label: 'Log in', onClick: onLogin }));
account.appendChild(
createButton({
size: 'small',
label: 'Sign up',
onClick: onCreateAccount,
primary: true,
})
);
}
wrapper.appendChild(account);
header.appendChild(wrapper);

return header;
};
27 changes: 27 additions & 0 deletions code/renderers/html/template/cli/ts-4-9/Page.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { Meta, StoryObj } from '@storybook/html';
import { within, userEvent } from '@storybook/testing-library';
import { createPage } from './Page';

const meta = {
title: 'Example/Page',
render: () => createPage(),
parameters: {
// More on how to position stories at: https://storybook.js.org/docs/7.0/html/configure/story-layout
layout: 'fullscreen',
},
} satisfies Meta;

export default meta;

export const LoggedOut: StoryObj = {};

// More on interaction testing: https://storybook.js.org/docs/7.0/html/writing-tests/interaction-testing
export const LoggedIn: StoryObj = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const loginButton = await canvas.getByRole('button', {
name: /Log in/i,
});
await userEvent.click(loginButton);
},
};
98 changes: 98 additions & 0 deletions code/renderers/html/template/cli/ts-4-9/Page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import './page.css';
import { createHeader } from './Header';

type User = {
name: string;
};

export const createPage = () => {
const article = document.createElement('article');
let user: User | undefined;
let header: HTMLElement | null = null;

const rerenderHeader = () => {
const wrapper = document.getElementsByTagName('article')[0];
wrapper.replaceChild(createHeaderElement(), wrapper.firstChild as HTMLElement);
};

const onLogin = () => {
user = { name: 'Jane Doe' };
rerenderHeader();
};

const onLogout = () => {
user = undefined;
rerenderHeader();
};

const onCreateAccount = () => {
user = { name: 'Jane Doe' };
rerenderHeader();
};

const createHeaderElement = () => {
return createHeader({ onLogin, onLogout, onCreateAccount, user });
};

header = createHeaderElement();
article.appendChild(header);

const section = `
<section>
<h2>Pages in Storybook</h2>
<p>
We recommend building UIs with a
<a
href="https://blog.hichroma.com/component-driven-development-ce1109d56c8e"
target="_blank"
rel="noopener noreferrer">
<strong>component-driven</strong>
</a>
process starting with atomic components and ending with pages.
</p>
<p>
Render pages with mock data. This makes it easy to build and review page states without
needing to navigate to them in your app. Here are some handy patterns for managing page data
in Storybook:
</p>
<ul>
<li>
Use a higher-level connected component. Storybook helps you compose such data from the
"args" of child component stories
</li>
<li>
Assemble data in the page component from your services. You can mock these services out
using Storybook.
</li>
</ul>
<p>
Get a guided tutorial on component-driven development at
<a href="https://storybook.js.org/tutorials/" target="_blank" rel="noopener noreferrer">
Storybook tutorials
</a>
. Read more in the
<a href="https://storybook.js.org/docs" target="_blank" rel="noopener noreferrer">docs</a>
.
</p>
<div class="tip-wrapper">
<span class="tip">Tip</span>
Adjust the width of the canvas with the
<svg width="10" height="10" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg">
<g fill="none" fillRule="evenodd">
<path
d="M1.5 5.2h4.8c.3 0 .5.2.5.4v5.1c-.1.2-.3.3-.4.3H1.4a.5.5 0
01-.5-.4V5.7c0-.3.2-.5.5-.5zm0-2.1h6.9c.3 0 .5.2.5.4v7a.5.5 0 01-1 0V4H1.5a.5.5 0
010-1zm0-2.1h9c.3 0 .5.2.5.4v9.1a.5.5 0 01-1 0V2H1.5a.5.5 0 010-1zm4.3 5.2H2V10h3.8V6.2z"
id="a"
fill="#999" />
</g>
</svg>
Viewports addon in the toolbar
</div>
</section>
`;

article.insertAdjacentHTML('beforeend', section);

return article;
};
Loading