Skip to content
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
45 changes: 45 additions & 0 deletions tools/generators/react-component/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# react-component

Workspace Generator for creating React Component within v9 package.

<!-- toc -->

- [Usage](#usage)
- [Examples](#examples)
- [Options](#options)
- [`name`](#name)
- [`project`](#project)

<!-- tocstop -->

## Usage

```sh
yarn nx workspace-generator react-component --help
```

Show what will be generated without writing to disk:

```sh
yarn nx workspace-generator react-component --dry-run
```

### Examples

```sh
yarn nx workspace-generator react-component
```

## Options

#### `name`

Type: `string`

The name of the component.

#### `project`

Type: `string`

The name of the project where the component will be generated.
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import * as React from 'react';
import { render } from '@testing-library/react';
import { isConformant } from '../../testing/isConformant';
import { <%= componentName %> } from './<%= componentName %>';

describe('<%= componentName %>', () => {
isConformant({
Component: <%= componentName %>,
displayName: '<%= componentName %>',
});

// TODO add more tests here, and create visual regression tests in /apps/vr-tests

it('renders a default state', () => {
const result = render(<<%= componentName %>>Default <%= componentName %></<%= componentName %>>);
expect(result.container).toMatchSnapshot();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import * as React from 'react';
import type { ForwardRefComponent } from '@fluentui/react-utilities';
import { use<%= componentName %>_unstable } from './use<%= componentName %>';
import { render<%= componentName %>_unstable } from './render<%= componentName %>';
import { use<%= componentName %>Styles_unstable } from './use<%= componentName %>Styles.styles';
import type { <%= componentName %>Props } from './<%= componentName %>.types';

/**
* <%= componentName %> component - TODO: add more docs
*/
export const <%= componentName %>: ForwardRefComponent<<%= componentName %>Props> = React.forwardRef((props, ref) => {
const state = use<%= componentName %>_unstable(props, ref);

use<%= componentName %>Styles_unstable(state);
return render<%= componentName %>_unstable(state);
});

<%= componentName %>.displayName = '<%= componentName %>';
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type { ComponentProps, ComponentState, Slot } from '@fluentui/react-utilities';

export type <%= componentName %>Slots = {
root: Slot<'div'>;
};

/**
* <%= componentName %> Props
*/
export type <%= componentName %>Props = ComponentProps<<%= componentName %>Slots> & {};

/**
* State used in rendering <%= componentName %>
*/
export type <%= componentName %>State = ComponentState<<%= componentName %>Slots>;
// TODO: Remove semicolon from previous line, uncomment next line, and provide union of props to pick from <%= componentName %>Props.
// & Required<Pick<<%= componentName %>Props, 'propName'>>
;
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * from './<%= componentName %>';
export * from './<%= componentName %>.types';
export * from './render<%= componentName %>';
export * from './use<%= componentName %>';
export * from './use<%= componentName %>Styles.styles';
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/** @jsxRuntime classic */
/** @jsx createElement */

import { createElement } from '@fluentui/react-jsx-runtime';
import { getSlotsNext } from '@fluentui/react-utilities';
import type { <%= componentName %>State, <%= componentName %>Slots } from './<%= componentName %>.types';

/**
* Render the final JSX of <%= componentName %>
*/
export const render<%= componentName %>_unstable = (state: <%= componentName %>State) => {
const { slots, slotProps } = getSlotsNext<<%= componentName %>Slots>(state);

// TODO Add additional slots in the appropriate place
return <slots.root {...slotProps.root} />;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import * as React from 'react';
import { getNativeElementProps } from '@fluentui/react-utilities';
import type { <%= componentName %>Props, <%= componentName %>State } from './<%= componentName %>.types';

/**
* Create the state required to render <%= componentName %>.
*
* The returned state can be modified with hooks such as use<%= componentName %>Styles_unstable,
* before being passed to render<%= componentName %>_unstable.
*
* @param props - props from this instance of <%= componentName %>
* @param ref - reference to root HTMLElement of <%= componentName %>
*/
export const use<%= componentName %>_unstable = (props: <%= componentName %>Props, ref: React.Ref<HTMLElement>): <%= componentName %>State => {
return {
// TODO add appropriate props/defaults
components: {
// TODO add each slot's element type or component
root: 'div',
},
// TODO add appropriate slots, for example:
// mySlot: resolveShorthand(props.mySlot),
root: getNativeElementProps('div', {
ref,
...props,
}),
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { makeStyles, mergeClasses } from '@griffel/react';
import type { SlotClassNames } from '@fluentui/react-utilities';
import type { <%= componentName %>Slots, <%= componentName %>State } from './<%= componentName %>.types';


export const <%= propertyName %>ClassNames:SlotClassNames<<%= componentName %>Slots> = {
root: 'fui-<%= componentName %>'
// TODO: add class names for all slots on <%= componentName %>Slots.
// Should be of the form `<slotName>: 'fui-<%= componentName %>__<slotName>`
};

/**
* Styles for the root slot
*/
const useStyles = makeStyles({
root: {
// TODO Add default styles for the root element
},

// TODO add additional classes for different states and/or slots
});

/**
* Apply styling to the <%= componentName %> slots based on the state
*/
export const use<%= componentName %>Styles_unstable = (state: <%= componentName %>State): <%= componentName %>State => {
const styles = useStyles();
state.root.className = mergeClasses(<%= propertyName %>ClassNames.root, styles.root, state.root.className);

// TODO Add class names to slots, for example:
// state.mySlot.className = mergeClasses(styles.mySlot, state.mySlot.className);

return state;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## Best practices

### Do

### Don't
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import * as React from 'react';
import { <%= componentName %>, <%= componentName %>Props } from '<%= npmPackageName %>';

export const Default = (props: Partial<<%= componentName %>Props>) => (
<<%= componentName %> {...props} />
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { <%= componentName %> } from '<%= npmPackageName %>';

import descriptionMd from './<%= componentName %>Description.md';
import bestPracticesMd from './<%= componentName %>BestPractices.md';

export { Default } from './<%= componentName %>Default.stories';

export default {
title: 'Preview Components/<%= componentName %>',
component: <%= componentName %>,
parameters: {
docs: {
description: {
component: [descriptionMd, bestPracticesMd].join('\n'),
}
}
}
};
Loading