From 80c627e4fb8d63f6bc3c10b02fb271840db3b56c Mon Sep 17 00:00:00 2001 From: Martin Bohal Date: Wed, 10 Jan 2024 11:50:17 +0100 Subject: [PATCH] Update blacklisted props that can be passed to native HTML elements using the transferProps principle Update src/components/_helpers/transferProps.js Co-authored-by: Adam Kudrna Update src/components/_helpers/transferProps.js Co-authored-by: Adam Kudrna Update src/components/_helpers/transferProps.js Co-authored-by: Adam Kudrna --- .../_helpers/__tests__/transferProps.test.js | 16 ++++-- src/components/_helpers/transferProps.js | 54 ++++++++++++++++--- 2 files changed, 58 insertions(+), 12 deletions(-) diff --git a/src/components/_helpers/__tests__/transferProps.test.js b/src/components/_helpers/__tests__/transferProps.test.js index dc0df6d1..69afcc31 100644 --- a/src/components/_helpers/__tests__/transferProps.test.js +++ b/src/components/_helpers/__tests__/transferProps.test.js @@ -5,7 +5,6 @@ describe('transferProps', () => { const props = { propA: 'value', propB: 'value', - propC: 'value', }; const expectedProps = { ...props }; @@ -14,13 +13,20 @@ describe('transferProps', () => { it('returns filtered props using always blacklisted props', () => { const props = { - children: 'value', className: 'value', - ref: 'value', - staticContext: 'value', + contentEditable: true, + propA: 'value', }; - const expectedProps = {}; + const expectedProps = { propA: 'value' }; + let errorString; + const originalConsoleError = console.error; + console.error = (error) => { + errorString = error; + }; expect(transferProps(props)).toEqual(expectedProps); + expect(errorString).toEqual('Invalid prop(s) supplied to the "transferProps" function: "className", "contentEditable"'); + + console.error = originalConsoleError; }); }); diff --git a/src/components/_helpers/transferProps.js b/src/components/_helpers/transferProps.js index 5e61f710..07880a76 100644 --- a/src/components/_helpers/transferProps.js +++ b/src/components/_helpers/transferProps.js @@ -1,7 +1,47 @@ -export const transferProps = ({ - children, - className, - ref, - staticContext, - ...restProps -}) => restProps; +/** + * Controls passing of props from the React component to the HTML element + * + * Sometimes it is useful to have a mechanism to pass props from the React component to a rendered HTML element. + * It enables making the component interactive and helps improve its accessibility. However some props should + * never be passed to the HTML element as it would break things. This function is used to filter out such props. + * + * When run in development mode, the function will log the error to the console if any invalid props are passed. + * + * @param props The props that were passed to the React component and were not used by it + * @returns The props to be passed to the HTML element + */ +export const transferProps = (props) => { + const { + children, + className, + contentEditable, + dangerouslySetInnerHTML, + ref, + staticContext, + style, + suppressContentEditableWarning, + ...restProps + } = props; + + if (process.env.NODE_ENV !== 'production') { + console.log('props', props); + const invalidProps = [ + 'children', // It is always either handled by the component itself or not supported. + 'className', // Classes are set by component authors, changing it arbitrarily might break things. + 'contentEditable', // Components are either interactive or not, changing it arbitrarily might break things. + 'dangerouslySetInnerHTML', // This might break things and allow for XSS attacks. + 'ref', // Forwarding `ref` is hardcoded and documented for each component. + 'staticContext', // In `react-router` (v4, v5) this is used during server side rendering, it makes no sense to pass it to a component. + 'style', // Styles are set by component authors, changing it arbitrarily might break things. + 'suppressContentEditableWarning', // Since setting `contentEditable` is not allowed, this is not needed. + ] + .filter((key) => props[key] !== undefined); + + if (invalidProps.length > 0) { + // eslint-disable-next-line no-console + console.error(`Invalid prop(s) supplied to the "transferProps" function: "${invalidProps.join('", "')}"`); + } + } + + return restProps; +};