UNPKG

9.74 kBJavaScriptView Raw
1"use strict";
2
3var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
5Object.defineProperty(exports, "__esModule", {
6 value: true
7});
8exports.Children = exports.default = void 0;
9
10var _react = _interopRequireDefault(require("react"));
11
12var _makeRequirable = require("./makeRequirable");
13
14/*
15 * The MIT License (MIT)
16 *
17 * Copyright (c) 2015 - present Instructure, Inc.
18 *
19 * Permission is hereby granted, free of charge, to any person obtaining a copy
20 * of this software and associated documentation files (the "Software"), to deal
21 * in the Software without restriction, including without limitation the rights
22 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
23 * copies of the Software, and to permit persons to whom the Software is
24 * furnished to do so, subject to the following conditions:
25 *
26 * The above copyright notice and this permission notice shall be included in all
27 * copies or substantial portions of the Software.
28 *
29 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
32 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
34 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
35 * SOFTWARE.
36 */
37var Children = {
38 /**
39 * Validate that the children of a component are one of the specified types.
40 *
41 * ```js
42 * import { Children } from '@instructure/ui-prop-types'
43 *
44 * class Example extends Component {
45 * static propTypes = {
46 * children: Children.oneOf([Foo, Bar, Baz])
47 * }
48 *
49 * render () {
50 * return <div>{this.props.children}</div>
51 * }
52 * }
53 * ```
54 *
55 * This will allow children such as:
56 *
57 * ```jsx
58 * <Example>
59 * <Foo />
60 * </Example>
61 * ```
62 *
63 * OR
64 *
65 * ```jsx
66 * <Example>
67 * <Bar />
68 * <Foo />
69 * </Example>
70 * ```
71 *
72 * But will fail on something like:
73 *
74 * ```jsx
75 * <Example>
76 * <h1>Example</h1>
77 * <Foo />
78 * </Example>
79 * ```
80 * @returns {Error} if validation failed
81 */
82 oneOf: function oneOf(validTypes) {
83 function validator(props, propName, componentName) {
84 var children = _react.default.Children.toArray(props[propName]);
85
86 var validTypeNames = validTypes.map(function (type) {
87 return type ? getDisplayName(type) : type;
88 });
89
90 for (var i = 0; i < children.length; i++) {
91 var child = children[i];
92
93 if (child && child.type) {
94 var childName = getDisplayName(child.type);
95
96 if (validTypeNames.indexOf(childName) < 0) {
97 return new Error("Expected one of ".concat(validTypeNames.join(', '), " in ").concat(componentName, " but found '").concat(childName, "'"));
98 }
99 } else if (child) {
100 return new Error("Expected one of ".concat(validTypeNames.join(', '), " in ").concat(componentName, " but found an element with unknown type: ").concat(child));
101 }
102 }
103 }
104
105 validator.isRequired = (0, _makeRequirable.makeRequirable)(validator);
106 return validator;
107 },
108
109 /**
110 * Ensures that there is exactly one of each specified child
111 *
112 * ```js
113 * import { Children } from '@instructure/ui-prop-types'
114 *
115 * class Example extends Component {
116 * static propTypes = {
117 * children: Children.oneOfEach([Foo, Bar, Baz])
118 * }
119 *
120 * render () {
121 * return <div>{this.props.children}</div>
122 * }
123 * }
124 * ```
125 *
126 * This will enforce the following:
127 *
128 * ```jsx
129 * <Example>
130 * <Foo />
131 * <Bar />
132 * <Baz />
133 * </Example>
134 * ```
135 * An error will be thrown
136 * - If any of the children are not provided (ex. Foo, Bar, but missing Baz)
137 * - If multiple children of the same type are provided (ex. Foo, Foo, Bar, and Baz)
138 *
139 * @param {Array} validTypes - Array of child types
140 * @returns {Error} if validation failed
141 */
142 oneOfEach: function oneOfEach(validTypes) {
143 return function (props, propName, componentName) {
144 var children = _react.default.Children.toArray(props[propName]);
145
146 var instanceCount = {};
147 var validTypeNames = validTypes.map(function (type) {
148 var typeName = getDisplayName(type);
149 instanceCount[typeName] = 0;
150 return typeName;
151 });
152
153 for (var i = 0; i < children.length; i++) {
154 var child = children[i];
155
156 if (child && child.type) {
157 var childName = getDisplayName(child.type);
158
159 if (validTypeNames.indexOf(childName) < 0) {
160 return new Error("Expected one of ".concat(validTypeNames.join(', '), " in ").concat(componentName, " but found '").concat(childName, "'"));
161 }
162
163 instanceCount[childName] = (instanceCount[childName] || 0) + 1;
164 } else if (child) {
165 return new Error("Expected one of ".concat(validTypeNames.join(', '), " in ").concat(componentName, " but found an element of unknown type: ").concat(child));
166 }
167 }
168
169 var errors = [];
170 Object.keys(instanceCount).forEach(function (childName) {
171 if (instanceCount[childName] > 1) {
172 errors.push("".concat(instanceCount[childName], " children of type ").concat(childName));
173 }
174
175 if (instanceCount[childName] === 0) {
176 errors.push("0 children of type ".concat(childName));
177 }
178 });
179
180 if (errors.length > 0) {
181 return new Error("Expected exactly one of each ".concat(validTypeNames.join(', '), " in ").concat(componentName, " but found:\n ").concat(errors.join('\n')));
182 }
183 };
184 },
185
186 /**
187 * Validate the type and order of children for a component.
188 *
189 * ```js
190 * import { Children } from '@instructure/ui-prop-types'
191 *
192 * class Example extends Component {
193 * static propTypes = {
194 * children: Children.enforceOrder([Foo, Bar, Baz])
195 * }
196 *
197 * render () {
198 * return <div>{this.props.children}</div>
199 * }
200 * }
201 * ```
202 *
203 * This will enforce the following:
204 *
205 * ```jsx
206 * <Example>
207 * <Foo />
208 * <Bar />
209 * <Baz />
210 * </Example>
211 * ```
212 *
213 * This validator will also allow various permutations of the order.
214 *
215 * ```js
216 * import { Children } from '@instructure/ui-prop-types'
217 *
218 * class Example extends Component {
219 * static propTypes = {
220 * children: Children.enforceOrder(
221 * [Foo, Bar, Baz],
222 * [Foo, Bar],
223 * [Bar, Baz],
224 * )
225 * }
226 *
227 * render () {
228 * return <div>{this.props.children}</div>
229 * }
230 * }
231 * ```
232 *
233 * This will enforce one of the following:
234 *
235 * ```jsx
236 * <Example>
237 * <Foo />
238 * <Bar />
239 * <Baz />
240 * </Example>
241 * ```
242 *
243 * OR
244 *
245 * ```jsx
246 * <Example>
247 * <Foo />
248 * <Bar />
249 * </Example>
250 * ```
251 *
252 * OR
253 *
254 * ```jsx
255 * <Example>
256 * <Bar />
257 * <Baz />
258 * </Example>
259 * ```
260 *
261 * @param {...Array} validTypeGroups One or more Arrays of valid types
262 * @returns {Error} if validation failed
263 */
264 enforceOrder: function enforceOrder() {
265 for (var _len = arguments.length, validTypeGroups = new Array(_len), _key = 0; _key < _len; _key++) {
266 validTypeGroups[_key] = arguments[_key];
267 }
268
269 function validateTypes(childNames, typeNames) {
270 for (var i = 0; i < childNames.length; i++) {
271 if (childNames[i] !== typeNames[i]) {
272 return false;
273 }
274 }
275
276 return true;
277 }
278
279 function formatGroupTypes(componentName, typeGroups) {
280 return typeGroups.map(function (types) {
281 return formatTypes(componentName, types);
282 }).join('\n\n');
283 }
284
285 function formatTypes(componentName, types) {
286 var children = types.map(function (type) {
287 if (type) {
288 return getDisplayName(type);
289 } else {
290 return '??';
291 }
292 }).map(function (name) {
293 return " <".concat(name, " />");
294 }).join('\n');
295 return "<".concat(componentName, ">\n").concat(children, "\n</").concat(componentName, ">");
296 }
297
298 function validator(props, propName, componentName) {
299 var childNames = _react.default.Children.toArray(props[propName]).map(function (child) {
300 if (child && child.type) {
301 return getDisplayName(child.type);
302 } else if (child) {
303 return null;
304 }
305 }); // Validate each group, if any of them are valid we're done
306
307
308 for (var i = 0; i < validTypeGroups.length; i++) {
309 var validTypeNames = validTypeGroups[i].map(function (type) {
310 if (type) {
311 return getDisplayName(type);
312 } else {
313 return '??';
314 }
315 });
316
317 if (validateTypes(childNames, validTypeNames)) {
318 return;
319 }
320 } // If we make it through the loop then children are not valid
321
322
323 return new Error("Expected children of ".concat(componentName, " in one of the following formats:\n ").concat(formatGroupTypes(componentName, validTypeGroups), "\n\n\n Instead of:\n ").concat(formatTypes(componentName, childNames)));
324 }
325
326 validator.isRequired = (0, _makeRequirable.makeRequirable)(validator);
327 return validator;
328 }
329}; // TODO: Remove when we further break up ui-utils and bringing this in no longer creates
330// a circular dep
331
332exports.Children = Children;
333
334var getDisplayName = function getDisplayName(Component) {
335 return typeof Component === 'string' ? Component : Component.displayName || Component.name;
336};
337
338var _default = Children;
339exports.default = _default;
\No newline at end of file