Skip to content

Latest commit

 

History

History
308 lines (239 loc) · 9.23 KB

CONTRIBUTION.md

File metadata and controls

308 lines (239 loc) · 9.23 KB

Setup

Install the dependencies with yarn

// install deps
yarn

// normal start, fast speed in develop component
yarn start

// start with docgen, that run when you write doc in component
yarn storybook:docgen

Coding style guide

Component file structure:

├── index.ts // All item reexport from here, no logic involved.
├── InlineEditable.tsx // The main logic file.
├── __stories__
   ├── InlineEditable.story.mdx // The component document page.
   ├── InlineEditable.story.tsx // The component storybook.
   └── __snapshots__
       └── InlineEditable.story.tsx.snap // Auto generate snapshot.
├── __tests__
   └── InlineEditable.test.tsx // Unit test for that component
├── styles
   ├── InlineEditableStyle.tsx // Main custom style file
   ├── index.ts // Re-export
   └── textFieldStyle.tsx // Addition style
└── utils // All token and related method put in here.
    ├── InlineEditableUtils.ts
    └── index.ts // Re-export

Main Logic File

type RcButtonColor = RcBaseColor<
  'primary' | 'secondary' | 'negative' | 'positive' | 'action' | 'neutral'
>;

type RcButtonColorLoadingMode = 'prefix' | 'replace' | 'suffix';

type RcButtonVariant = 'text' | 'outlined' | 'contained' | 'plain';

type RcButtonRadius = keyof RcTheme['radius'];

type RcButtonProps = {
  /** size of button, default is `large` */
  size?: RcButtonSize;
  /** variant of button, default is `contained` */
  variant?: RcButtonVariant;
  /** color of button */
  color?: RcPaletteKeys | RcButtonColor;
  /** is button loading */
  loading?: boolean;
  /** loading mode with button, default is `replace` */
  loadingMode?: RcButtonColorLoadingMode;
  /** Props send to `RcCircularProgress` when loading is `true` */
  CircularProgressProps?: RcCircularProgressProps;
  /** component for render root button, default is `button` */
  component?: React.ElementType;
  /** border radius for button */
  radius?: RcButtonRadius;
  /** should keep elevation when type is `container` */
  keepElevation?: boolean;
  /** @deprecated Icon, please use startIcon with `<RcIcon />` */
  IconProps?: RcIconProps;
} & RcBaseProps<
  ComponentProps<typeof MuiButton>,
  | 'color'
  | 'size'
  | 'variant'
  | 'title'
  // * use disableFocusRipple, so omit that
  | 'focusRipple'
>;

const _RcButton = forwardRef<any, RcButtonProps>(
  (inProps: RcButtonProps, ref) => {
    const props = useThemeProps({ props: inProps, name: 'RcButton' });
    const {
      children: childrenProp,
      classes: classesProp,
      startIcon: startIconProp,
      endIcon: endIconProp,
      // omit all your custom props
      loading,
      loadingMode,
      IconProps,
      size,
      color,
      CircularProgressProps,
      variant,
      radius,
      keepElevation,
      ...rest
    } = props;

    ...

    const classes = useMemo(
      () => combineClasses(RcButtonClasses, classesProp),
      [classesProp],
    );

    return (
      <MuiButton
        ref={buttonRef}
        startIcon={startIcon}
        endIcon={endIcon}
        classes={classes}
        {...rest}
      >
        ...
      </MuiButton>
    );
  },
);

const RcButton = styled(_RcButton)`
  ${buttonStyle}
`;

RcButton.defaultProps = {
  size: 'large',
  color: 'primary',
  variant: 'contained',
  loadingMode: 'replace',
};

RcButton.displayName = 'RcButton';

export { RcButton };

export type {
  RcButtonColor,
  RcButtonColorLoadingMode,
  RcButtonProps,
  RcButtonSize,
  RcButtonVariant,
};
  1. Import import React, { FunctionComponent } from 'react'; and not using React.xxx, other import should keep short and clear.

  2. The component props related type should be the top of file after all import and naming for [Name]Props.

  3. Here is some Base type from foundation, you should use that to unified token, like size, color, you need using BaseSize or BaseColor to pick that.

  4. Custom props you should add comment with and show the default value.

  5. Join with Material Props with & by BaseProps and Omit props you want omit in the second argument, and that should join after our addition props.

  6. Destructure props you need for logic inside Component not on the function argument, and pass whole props into Styled[Name], keep all props go into Styled.

  7. Custom class should using utils set classes like RcButtonClasses, that implement will in the Token Utils section.

  8. All props we custom cover with need combine with out side pass in props, like classes, user can add custom class name for this component, if we don't using combineClasses to combine those props, that will make outside props not working.

  9. When custom event should use combineProps to wrapper methods, that will combine two method together,

    combineProps({ onClick: onClickProp }, { onClick: handleClose });
  10. final export with styled to contain this private component _[Name] assign to FunctionComponent<[Name]Props>, that for us can using this component in other styled to styled that.

  11. Component need assign displayName for Storybook using show with correct name example [Name].displayName = '[Name]';

  12. At Main Component file, All export in main file should put in the bottom of file.(Other utils file can export directly)

Styled Component


1. Using styled from foundation.

```ts
import { styled, css, RcThemedStyled } from '../../../../foundation';
  1. Pick material props from forward component and remove all props which should not pass to material.
// ButtonStyle.tsx
export const buttonStyle: RcThemedStyled<RcButtonProps, any> = (props) => {
  const { variant, size, radius: radiusProp, keepElevation } = props;
  const plainTextColor = plainButtonTextColor(props);

  const iconSpace = spacing(RcButtonIconSpace[size!]);

  const isPlain = variant === 'plain';

  return css`
    ...some style
  `;
};

// Button.tsx
const RcButton = styled(_RcButton)`
  ${buttonStyle}
`;
  1. All method contain in styled style template block will auto pass ({ theme }) after function return with function.
    • like above palette2('primary', 'main'), it will return ({ theme }: ThemeProps) => any, that will auto be pass with theme, don't pass manually like palette2('primary', 'main')({theme}).
  • All token styled utils contain in foundation folder, we should using that from there.

    import { palette2, spacing, typography } from '../../../../foundation';

    Custom class should get from utils, don't use plain string like below.

      &.${ButtonClasses.contained} {
        ...;
      }
  1. If you want to pick method out of styled block, you need using RcThemedStyled to declare method, like below
    const buttonHoverColor: RcThemedStyled<RcButtonProps, any> = ({
      color,
      theme,
    }) => setOpacity(palette2('neutral.b02'), '08');
    RcThemedStyled generic type first arg is the method argument type, and the second type is that return type, default with (themeOptions: RcThemeProps) => any, that will auto run with ({ theme }).

Token Utils

import { palette, Classes, PaletteType, UnitMap } from '../../../../foundation';
import { ButtonColor, ButtonProps, ButtonSize } from '../Button';

export const ButtonClasses = Classes<ButtonProps>(
  ['disabled', 'contained', 'text', 'outlined'],
  'Button',
);

export const ButtonColors: UnitMap<ButtonColor, PaletteType> = {
  primary: palette2('primary', 'main'),
  secondary: palette2('secondary', 'main'),
  negative: palette2('semantic', 'positive'),
  positive: palette2('semantic', 'negative'),
  action: palette2('common', 'white'),
};

export const ButtonSizes: UnitMap<ButtonSize> = {
  medium: '36px',
  large: '40px',
};
  1. Classes related should use to contain, that will make all using class to unified [alias]-[classType] like above disabled will be: Button-disabled, that will make us easy to debug and identify.
    Classes<[Name]Props>([...classTypes], 'alias')`
  2. Token relate should using UnitMap<[Name][TokenName]> to unified size and check we has implement with, like size and color, and the second type will assign for every item type.

Use Icon

You can import svg icon components from icon/xxx.tsx like below,

import { ArrowDown } from '@ringcentral/juno-icon';

Debug with vscode

Launch Debug

ctrl + shift + p

Typing task and choice Run Storybook, that will serve the application.

Press F5 to run Launch Chrome and you can debug with vscode.

Attach Debug

Before you using attach debug, you should close all of chrome, and using terminal to open chrome.

ctrl + shift + p

Typing task and choice Run app, to run app.

Press F5 to run Attach Browser and you can using vscode debug in the IDE now.

Typescript

https://jkchao.github.io/typescript-book-chinese https://www.typescriptlang.org/docs/handbook/utility-types.html

Testing

yarn test

Git flow

  1. checkout branch from main.
  2. commit with message like feat(Ticket-Number): [Update-scope] what change you made, like chore(RCUI-100): [Snapshot] update snapshot.
  3. We test components change base on snapshot, run yarn update-snapshot to update snapshot before you push.