export function createShorthandInternal()

in packages/fluentui/react-northstar/src/utils/factories.ts [89:240]


export function createShorthandInternal<P>({
  Component,
  mappedProp,
  mappedArrayProp,
  value,
  options = {},
  allowsJSX = true,
}: {
  Component: React.ElementType<P>;
  mappedProp?: string;
  mappedArrayProp?: string;
  allowsJSX?: boolean;
  value?: ShorthandValue<P>;
  options?: CreateShorthandOptions<P>;
}) {
  if (!ReactIs.isValidElementType(Component)) {
    throw new Error('createShorthand() Component must be a string or function.');
  }

  // short circuit noop values
  const valIsNoop = _.isNil(value) || typeof value === 'boolean';
  if (valIsNoop && !options.render) return null;

  const valIsPrimitive = typeof value === 'string' || typeof value === 'number';
  const valIsPropsObject = _.isPlainObject(value);
  const valIsArray = _.isArray(value);
  const valIsReactElement = React.isValidElement(value);

  // unhandled type warning
  if (process.env.NODE_ENV !== 'production') {
    const displayName = typeof Component === 'string' ? Component : Component.displayName;

    if (!valIsPrimitive && !valIsPropsObject && !valIsArray && !valIsReactElement && !valIsNoop) {
      /* eslint-disable-next-line no-console */
      console.error(
        [
          `The shorthand prop for "${displayName}" component was passed a JSX element but this slot only supports string|number|object|array|ReactElements.`,
          ' Use null|undefined|boolean for none.',
          ` Received: ${value}`,
        ].join(''),
      );
    }

    if (!allowsJSX && valIsReactElement) {
      /* eslint-disable-next-line no-console */
      console.error(
        [
          `The shorthand prop for "${displayName}" component was passed a JSX element but this slot only supports string|number|object|array.`,
          ' Use null|undefined|boolean for none.',
          ` Received: ${value}`,
        ].join(''),
      );
    }
  }

  // ----------------------------------------
  // Build up props
  // ----------------------------------------
  const defaultProps = options.defaultProps ? options.defaultProps() || ({} as Props<P>) : ({} as Props<P>);

  // User's props
  const usersProps =
    (valIsReactElement && ({} as Props<P>)) || (valIsPropsObject && (value as Props<P>)) || ({} as Props<P>);

  // Override props
  const overrideProps: Props<P> =
    typeof options.overrideProps === 'function'
      ? (options.overrideProps({ ...defaultProps, ...usersProps }) as Props<P>)
      : (options.overrideProps as Props<P>) || ({} as Props<P>);

  // Merge props
  const props: Props<P> = { ...defaultProps, ...usersProps, ...overrideProps };

  const mappedHTMLProps = mappedProps[overrideProps.as || defaultProps.as];

  // Map prop for primitive value
  if (valIsPrimitive || valIsReactElement) {
    (props as any)[mappedHTMLProps || mappedProp || 'children'] = value;
  }

  // Map prop for array value
  if (valIsArray) {
    (props as any)[mappedHTMLProps || mappedArrayProp || 'children'] = value;
  }

  // Merge className
  if (defaultProps.className || overrideProps.className || usersProps.className) {
    const mergedClassesNames = cx(defaultProps.className, overrideProps.className, usersProps.className);
    (props as any).className = _.uniq(mergedClassesNames.split(' ')).join(' ');
  }

  // Merge style
  if (defaultProps.style || overrideProps.style || usersProps.style) {
    (props as any).style = { ...defaultProps.style, ...usersProps.style, ...overrideProps.style };
  }

  // Merge styles
  if (defaultProps.styles || overrideProps.styles || usersProps.styles) {
    (props as any).styles = mergeStyles(defaultProps.styles, usersProps.styles, overrideProps.styles);
  }

  // ----------------------------------------
  // Get key
  // ----------------------------------------
  const { generateKey = true } = options;

  // Use key or generate key
  if (generateKey && _.isNil(props.key)) {
    if (valIsPrimitive) {
      // use string/number shorthand values as the key
      (props as any).key = value;
    }

    if (valIsReactElement) {
      // use the key from React Element
      const elementKey = (value as React.ReactElement).key;
      // <div /> - key is not passed as will be `null`
      // <div key={null} /> - key is passed as `null` and will be stringified
      const isNullKey = elementKey === null;

      if (!isNullKey) {
        (props as any).key = elementKey;
      }
    }
  }

  // Remove the kind prop from the props object
  delete props.kind;

  // ----------------------------------------
  // Create Element
  // ----------------------------------------
  const { render } = options;
  if (render) {
    return render(Component, props);
  }

  if (typeof props.children === 'function') {
    return props.children(Component, { ...props, children: undefined });
  }

  if (!allowsJSX && valIsReactElement) {
    return null;
  }

  // Create ReactElements from built up props
  if (valIsPrimitive || valIsPropsObject || valIsArray || valIsReactElement) {
    return React.createElement(Component, props);
  }

  return null;
}