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;
}