@carbon/ibm-products
Version:
Carbon for IBM Products
192 lines (178 loc) • 7.49 kB
JavaScript
/**
* Copyright IBM Corp. 2020, 2025
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/
;
require('react');
var index = require('../../../_virtual/index.js');
var pconsole = require('./pconsole.js');
// Copyright IBM Corp. 2020, 2021
//
// This source code is licensed under the Apache-2.0 license found in the
// LICENSE file in the root directory of this source tree.
//
// helper functions for component props
/**
* Prepare a set of props, or prop types or default props, merging values
* from one or more sets and optionally blocking keys which should not be
* passed. Returns the prepared set of props. Does not modify any of the
* objects passed.
*
* @param {{} | '' | ['']} values One or more sets of keys and values to be
* merged, or names of keys to be blocked. Each parameter that is an object is
* treated as keys and values to be merged, and each parameter that is a string
* or an array of strings is treated as keys to be blocked.
*
* Examples:
* const props = { a: 3, c: 4, d: 5 };
*
* * prepareProps(props) -> { a: 3, c: 4, d: 5 }
* * prepareProps(props, 'c') -> { a: 3, d: 5 }
* * prepareProps(props, ['a', 'c', 'e']) -> { d: 5 }
*
* * prepareProps({ a: 1, b: 2 }, props) -> { a: 3, b: 2, c: 4, d: 5 }
* * prepareProps({ a: 1, b: 2 }, props, ['a', 'c']) -> { b: 2, d: 5 }
*
* * prepareProps(props, { c: 6 }) -> { a: 3, c: 6, d: 5 }
* * prepareProps(props, 'a', { c: 6 }) -> { c: 6, d: 5 }
*/
const prepareProps = function () {
// Convert any string or array arg into an object with nulls as values
const toNulls = arg => typeof arg === 'string' ? {
[arg]: null
} : Array.isArray(arg) ? Object.fromEntries(arg.map(key => [key, null])) : arg;
// Merge all the args from left to right
for (var _len = arguments.length, values = new Array(_len), _key = 0; _key < _len; _key++) {
values[_key] = arguments[_key];
}
const merged = Object.assign({}, ...values.map(toNulls));
// Now strip any keys whose final value is null
return Object.entries(merged).reduce((result, _ref) => {
let [key, value] = _ref;
if (value !== null) {
result[key] = value;
}
return result;
}, {});
};
// Determine whether a named prop in a set of props has been given a value.
// null and undefined do not count as values, but anything else does. If the
// prop is 'children', then an array of null/undefined also does not count as
// a value, but anything else does.
const propHasValue = (props, propName) => {
let result = props[propName] !== null && props[propName] !== undefined;
if (result && propName === 'children' && Array.isArray(props[propName])) {
result = false;
for (let i = 0; !result && i < props[propName].length; i++) {
result = props[propName][i] !== null && props[propName][i] !== undefined;
}
}
return result;
};
/**
* A prop-types type checker that marks a prop as deprecated.
* @param {} validator The prop-types validator for the prop as it should be
* used if it weren't deprecated. If this validator produces type checking
* errors they will be reported as usual.
* @param {*} additionalInfo One or more sentences to be appended to the
* deprecation message to explain why the prop is deprecated and/or what should
* be used instead.
* @returns Any type checking error reported by the validator, or null.
*/
const deprecateProp = (validator, additionalInfo) => (props, propName, comp, loc, propFullName, secret) => {
if (propHasValue(props, propName)) {
pconsole.default.warn(`The ${loc} \`${propFullName || propName}\` of \`${comp}\` has been deprecated and will soon be removed. ${additionalInfo}`);
}
return validator(props, propName, comp, loc, propFullName, secret);
};
/**
* A prop-types validation function that takes an array of type checkers and
* requires prop values to satisfy all of the type checkers. This can be useful
* to combine custom validation functions with regular prop types, or for
* combining inherited prop-types from another component with tighter
* requirements.
*
* Examples:
*
* MyComponent.propTypes = {
*
* foo: allPropTypes([
* customValidationFunction,
* PropTypes.arrayOf(
* PropTypes.shape({
* text: PropType.string
* })
* )
* ]),
*
* kind: allPropTypes([
* Button.propTypes.kind,
* PropTypes.oneOf(['primary', 'secondary'])
* ]),
*
* }
*/
const allPropTypes = pconsole.default.shimIfProduction(arrayOfTypeCheckers => {
if (!Array.isArray(arrayOfTypeCheckers)) {
pconsole.default.error('Warning: Invalid argument supplied to allPropTypes, expected an instance of array.');
return pconsole.default.noop;
}
for (let i = 0; i < arrayOfTypeCheckers.length; i++) {
if (typeof arrayOfTypeCheckers[i] !== 'function') {
pconsole.default.error(`Invalid argument supplied to allPropTypes. Expected an array of check functions, but received ${arrayOfTypeCheckers[i]} at index ${i}.`);
return pconsole.default.noop;
}
}
const checkType = function () {
for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
args[_key2] = arguments[_key2];
}
let error = null;
arrayOfTypeCheckers.some(checker => error = checker(...args));
return error;
};
checkType.isRequired = (props, propName, comp, loc, propFullName, secret) => {
const prop = propFullName || propName;
return props[prop] == null ? new Error(`The ${loc} \`${prop}\` is marked as required in \`${comp || '<<anonymous>>'}\`, but its value is \`${props[prop] === null ? 'null' : 'undefined'}\`.`) : checkType(props, prop, comp, loc, propFullName, secret);
};
return checkType;
});
/**
* A prop-types validation function that takes a type checkers and a condition
* function and invokes either the type checker or the isRequired variant of
* the type checker according to whether the condition function returns false
* or true when called with the full set of props. This can be useful to make
* a prop conditionally required. The function also has a decorate function
* which can be used to add isRequiredIf to any existing type which already has
* an isRequired variant, and this is automatically applied to the simple type
* checkers in PropTypes when this props-helper module is imported. The second
* example produces better results with DocGen and Storybook.
*
* Examples:
*
* MyComponent1.propTypes = {
* showFoo: PropTypes.bool,
* fooLabel: isRequiredIf(PropTypes.string, ({ showFoo }) => showFoo),
* }
*
* MyComponent2.propTypes = {
* showBar: PropTypes.bool,
* barLabel: PropTypes.string.isRequired.if(({ showBar }) => showBar),
* }
*
*/
const isRequiredIf = (checker, conditionFn) => (props, propName, componentName, location, propFullName, secret) => (conditionFn(props) ? checker.isRequired : checker)(props, propName, componentName, location, propFullName, secret);
isRequiredIf.decorate = checker => {
checker.isRequired.if = pconsole.default.isProduction ? pconsole.default.noop : isRequiredIf.bind(null, checker);
};
for (const checker in index.default) {
if (index.default[checker].isRequired) {
isRequiredIf.decorate(index.default[checker]);
}
}
exports.allPropTypes = allPropTypes;
exports.deprecateProp = deprecateProp;
exports.isRequiredIf = isRequiredIf;
exports.prepareProps = prepareProps;