UNPKG

@carbon/ibm-products

Version:

Carbon for IBM Products

192 lines (178 loc) 7.49 kB
/** * 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. */ 'use strict'; 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;