Contributing to react-simplikit
react-simplikit
is designed to encourage contributions from anyone. If you'd like to contribute, please follow the guide below.
Implementation Contribution
When contributing implementations, add them to the appropriate directory based on their type (components
, hooks
, or utils
). Each implementation must include the following elements:
- Implementation
- Test Code
- JSDoc
TIP
Do I need to write documentation?
No, you don't need to write documentation separately. Instead, please write detailed JSDoc comments. Once your PR is merged, English and Korean documentation will be automatically generated based on the JSDoc, and a PR for adding the documentation will be automatically created.
Writing Implementations
You must follow react-simplikit
's Design Principles. We don't provide implementations that depend on specific libraries or are tightly coupled with React's lifecycle. Please write implementations that adhere to these design principles.
Writing JSDoc
All implementations must include JSDoc comments. These provide hints when using the implementation and play a crucial role in generating documentation. JSDoc comments must include @description
and @example
, and if there are parameters or return values, they should include @param
and @returns
.
JSDoc writing rules must be followed for accurate documentation generation. If JSDoc validation fails, CI might fail.
JSDoc must be written in English.
@description
: A required tag that clearly explains the implementation's functionality or role.@example
: A required tag that shows example code demonstrating how to use the implementation.@param
: Write the parameter's name and description. Must be included if the implementation has parameters.For required parameters:
@param {<type>} <parameter name> - <parameter description>
For optional parameters:
@param {<type>} [<parameter name>] - <parameter description>
For object parameters, both the object itself and its properties need
@param
tags.tstype Props = { name: string; age: number; nickname?: string; company: { name: string; address?: string; }; paymentMethod?: { type: 'card' | 'account'; number?: string; }; }; /** * @param {string} name - Name of the user. * @param {number} age - Age of the user. * @param {string} [nickname] - Nickname of the user. * @param {Object} company - Company information of the user. * @param {string} company.name - Name of the company. * @param {string} [company.address] - Address of the company. * @param {Object} [paymentMethod] - Payment information of the user. * @param {string} [paymentMethod.type] - Payment method. * @param {string} [paymentMethod.number] - Card or account number. */
This JSDoc will be converted into the following documentation.
- namerequired · string
Name of the user.
- agerequired · number
Age of the user.
- nicknamestring
Nickname of the user.
- companyrequired · Object
Company information of the user.
- company.namerequired · string
Name of the company.
- company.addressstring
Address of the company.
- company.namerequired · string
- paymentMethodObject
Payment information of the user.
- paymentMethod.typerequired · string
Payment method.
- paymentMethod.numberstring
Card or account number.
- paymentMethod.typerequired · string
- namerequired · string
@returns
: Write the return value's name and description. Must be included if the implementation has return values.Format:
@returns {<type>} <return value description>
For object or tuple return values, include descriptions for each member.
tstype ReturnValue = [string, () => void]; /** * @returns {[value: string, onChange: () => void]} A tuple containing: * - value `string` - The value of the input. * - onChange `() => void` - A function to update the value. */
This JSDoc will be converted into the following documentation.
- [value: string, onChange: () => void]
A tuple containing:
- valuestring
The value of the input.
- onChange() => void
A function to update the value.
- valuestring
Object-type return values can be written in a similar way.
tstype ReturnValue = { value: string; onChange: () => void }; /** * @returns {Object} An object containing: * - value `string` - The value of the input. * - onChange `() => void` - A function to update the value. */
This JSDoc will be converted into the following documentation.
- Object
An object containing:
- valuestring
The value of the input.
- onChange() => void
A function to update the value.
- valuestring
- [value: string, onChange: () => void]
Writing Test Code
All implementations must include test code, written with the same name as the implementation. Test coverage must always reach 100%. Use the following command to verify coverage:
yarn test:coverage
Please verify safe operation in SSR environments
All react-simplikit
implementations use special rendering functions to verify safe operation in SSR environments.
Component Testing
tsxit('is safe on server side rendering', () => { // renderSSR.serverOnly is a method that renders the component in the server environment. // In this environment, hooks like useEffect are not executed, and objects like window or document are not available, causing errors. renderSSR.serverOnly(() => ( <Component> <div>Test Content</div> </Component> )); expect(screen.getByText('Test Content')).toBeInTheDocument(); }); it('should render children correctly', async () => { // renderSSR is a method that renders the component in the client environment. // However, if the HTML rendered on the server and the HTML rendered on the client are different, hydration mismatch errors will occur. await renderSSR(() => ( <Component> <div>Test Content</div> </Component> )); expect(screen.getByText('Test Content')).toBeInTheDocument(); }); it('should hydration mismatch error occured', async () => { // This test code will fail due to a hydration mismatch error. await renderSSR(() => ( <Component> <div>Test Content</div> <div>{Math.random()}</div> </Component> )); expect(screen.getByText('Test Content')).toBeInTheDocument(); });
Hook Testing
tsit('is safe on server side rendering', () => { // renderHookSSR.serverOnly is a method that renders the hook in the server environment. // In this environment, hooks like useEffect are not executed, and objects like window or document are not available, causing errors. const result = renderHookSSR.serverOnly(() => useToggle(true)); const [bool] = result.current; expect(bool).toBe(true); }); it('should initialize with the default value true', async () => { const { result } = await renderHookSSR(() => useToggle(true)); const [bool] = result.current; expect(bool).toBe(true); });
Deployment
When changes are merged into the main branch, deployment happens automatically. You can view the deployment results in GitHub Actions.
Documentation Contribution
There are no specific conditions for contributing to documentation. If you find incorrect information, poor translations, or have additional content to add, feel free to make edits. Please write documentation clearly and concisely from the reader's perspective.
Scaffolding
There's a command that creates the minimum skeleton for contributions. Use the following command to create an implementation folder with a basic structure:
yarn run scaffold <name> --type <type>
type
: Implementation type, must be one ofcomponent
,hook
, orutil
.name
: Name of the implementation.
Example
yarn run scaffold Button --type component
This command creates three files in the src/components/Button
folder:
/**
* @description
* <description-here>
*
* @param {<param-type>} <param-name> - <param-description>
* @param {<param-type>} [<param-name>] - <optional-param-description>
*
* @returns {<return-type>} <return-description>
* - <member-description> `<member-name>` - <member-description>
*
* @example
* <example-code>
*/
export function Button() {
// TODO: Implement Button
}
import { describe, expect, it } from 'vitest';
import { renderSSR } from '../../_internal/test-utils/renderSSR.tsx';
import { Button } from './Button.tsx';
describe('Button', () => {
it('is safe on server side rendering', async () => {
const result = renderSSR.serverOnly(() => <Button />);
expect(true).toBe(true);
});
it('should work', async () => {
const result = renderSSR.serverOnly(() => <Button />);
expect(true).toBe(true);
});
});
export { Button } from './Button.tsx';
TIP
You can also use these shortcuts:
yarn run scaffold Button --t c // Create component
yarn run scaffold useButton --t h // Create hook
yarn run scaffold getButton --t u // Create util