Skip to content

Architecture Decision Records

Wojciech Kudyniuk edited this page Sep 7, 2023 · 4 revisions

ADR 1 - Use React.FC

// ✅ use FC imported from react 
export const Avatar: FC<AvatarProps> = (props) => ...

// ⛔ props typing
export const Avatar = (props: AvatarProps): JSX.Element => ...

ADR 2 - Extra Props should not extend Component Props

// ✅ AvatarProps extends MarginProps
export const Avatar: FC<AvatarProps & MarginProps> = (props) => ...

// ⛔ MarginProps are part of AvatarProps
export type AvatarProps = {/* propsTypes */} & MarginProps;

ADR 3 - Use UpperCamelCase to name components in Storybook

// ✅ TextInput name
const meta = {
  title: 'Components/TextInput'
}

// ⛔ Text Input
const meta = {
  title: 'Components/Text Input'
}

ADR 4 - Create stylesBuilder to build correct styles, wrap it with React.useMemo

// ✅ usage
const styles = useMemo(() => stylesBuilder(intent, custom), [intent, custom]);

// ✅ stylesBuilder.ts
type BadgeStylesBuilder = {
  container: BaseProps;
  iconContainer: BaseProps;
  label: BaseProps;
};

export const stylesBuilder = (
  custom: BadgeProps['custom'],
  intent: BadgeIntent,
  emphasis: BadgeEmphasis,
  hasLabel?: boolean,
  appearance?: BadgeAppearance,
): BadgeStylesBuilder => {
  const {
    innerElements,
    hasLabel: hasLabelStyles,
    intent: containerIntent,
    appearance: containerAppearance,
    ...container
  } = merge(defaultConfig, custom);

  const containerStyles = appearance
    ? containerAppearance[appearance][emphasis]
    : containerIntent[intent][emphasis];

  return {
    container: {
      ...container,
      ...containerStyles,
      ...(hasLabel ? hasLabelStyles : {}),
    },
    iconContainer: innerElements.iconContainer,
    label: innerElements.label,
  };
};

ADR 5 - Export props and component from index.ts

// ✅ index.ts
export { Avatar } from './Avatar';
export type { AvatarProps } from './Avatar.props';

ADR 6 - custom schema

export type BadgeConfig = {
  appearance?: Partial<
    Record<
      BadgeAppearance,
      { emphasis?: Partial<Record<'high' | 'medium' | 'low', BaseProps>> }
    >
  >;
  intent?: Partial<
    Record<
      BadgeIntent,
      { emphasis?: Partial<Record<'high' | 'medium' | 'low', BaseProps>> }
    >
  >;
  hasLabel?: BaseProps;
  innerElements?: {
    label?: BaseProps;
    iconContainer?: BaseProps;
  };
} & BaseProps;

export const defaultConfig = {
  /** CSS styles */
  hasLabel: {
    /** CSS styles */
  },
  appearance: {
    blue: {
      emphasis: {
        high: {
          /** CSS styles */
        },
        medium: {
          /** CSS styles */
        },
        low: {
          /** CSS styles */
        },
      },
    },
  },
  intent: {
    neutral: {
      emphasis: {
        high: {
          /** CSS styles */
        },
        medium: {
          /** CSS styles */
        },
        low: {
          /** CSS styles */
        },
      },
    },
  },
  innerElements: {
    label: {
      /** CSS styles */
      appearance: {
        blue: {
          high: {
            /** CSS styles */
          },
          medium: {
            /** CSS styles */
          },
          low: {
            /** CSS styles */
          },
        },
      },
    },
    iconContainer: {
      /** CSS styles */
    },
  },
} satisfies BadgeConfig;

ADR 7 - intent, appearance and emphasis type should be specific for each component

in progress

ADR 8 - props priority in styles config.

appearance > intent > emphasis

// ✅ correct
const defaultConfig = {
  blue: { // appearance 
    success: { // intent
      low: { // emphasis
        border: '1px solid red',
      },
    },
    warning: {
      hight: {
        border: '1px solid red',
      },
    },
  },
};