From ff27f02f3d3aef6a4a16f816b20362656d3233c5 Mon Sep 17 00:00:00 2001 From: Vlad Ifrim Date: Sun, 25 Feb 2024 19:35:19 +0200 Subject: [PATCH] fixes eslint issues --- .gitignore | 1 + package-lock.json | 62 ++-- package.json | 4 +- .../Accordion/js/AccordionItem.test.js | 10 +- .../Accordion/js/AccordionItemTrigger.js | 1 + src/components/Button/Button.test.js | 16 +- src/components/Button/index.js | 12 +- .../Collapsible/Collapsible.test.js | 13 +- src/components/Confirm/Confirm.test.js | 45 +-- src/components/Confirm/index.js | 4 +- .../CopyToClipboard/CopyToClipboard.test.js | 21 +- src/components/CopyToClipboard/index.js | 10 +- .../FixedWrapper/FixedWrapper.test.js | 8 +- .../InfiniteScroller/InfiniteScroller.test.js | 28 +- src/components/List/List.test.js | 20 +- src/components/List/index.js | 6 +- src/components/List/js/ListItem.js | 5 +- src/components/Message/Message.test.js | 10 +- src/components/Modal/Modal.test.js | 22 +- src/components/Panel/Panel.test.js | 38 ++- src/components/Panel/index.js | 33 +- src/components/Panel/js/PanelFooter.js | 4 +- src/components/Panel/js/PanelHeader.js | 22 +- src/components/Pill/index.js | 6 +- .../RevealPanel/js/RevealPanelService.js | 74 ++--- src/components/Table/Table.test.js | 58 ++-- src/components/Table/index.js | 68 ++-- src/components/Table/js/body.js | 5 +- src/components/Table/js/cell.js | 6 +- src/components/Table/js/header-cell.js | 4 +- src/components/Table/js/header.js | 6 +- src/components/Table/js/row.js | 15 +- src/components/Tabs/Tabs.test.js | 74 ++--- src/components/Tabs/index.js | 57 ++-- src/components/Tabs/js/Tab.js | 40 ++- src/components/Tabs/js/TabContent.js | 13 +- .../TextEllipsis/TextEllipsis.test.js | 5 +- src/components/Toast/Toast.test.js | 14 +- src/components/Toast/js/ToastContainer.js | 10 +- src/components/Toast/js/ToastMessage.js | 9 +- src/components/Tooltip/Tooltip.test.js | 31 +- src/form/components/Checkbox/Checkbox.test.js | 29 +- src/form/components/Checkbox/index.js | 93 +++--- .../components/CodeEditor/CodeEditor.test.js | 52 ++- src/form/components/Combobox/index.js | 302 +++++++++--------- .../components/Combobox2/Combobox2.test.js | 143 +++++---- src/form/components/Combobox2/index.js | 19 +- src/form/components/Combobox2/js/List.js | 8 +- src/form/components/Combobox2/js/Value.js | 25 +- .../components/DatePicker/DatePicker.test.js | 55 ++-- src/form/components/DatePicker/index.js | 1 + src/form/components/Dropdown/Dropdown.test.js | 50 +-- src/form/components/Dropdown/index.js | 1 + src/form/components/EditableList/index.js | 211 ++++++------ .../EditableList/js/EditableListForm.js | 91 +++--- src/form/components/EditableList2/Header.js | 38 +-- .../EditableList2/with-validation.js | 9 +- src/form/components/Input/index.js | 111 +++---- src/form/components/Input2/index.js | 2 +- .../StringBuilder/StringBuilder.test.js | 25 +- .../StringBuilder/js/invalid-token.js | 5 +- .../StringBuilder/js/options-list.js | 104 +++--- .../components/StringBuilder/js/service.js | 1 - .../StringBuilder/js/string-builder-footer.js | 4 +- .../StringBuilder/js/string-input.js | 4 +- .../StringBuilder/js/tokenized-string.js | 6 +- src/form/components/Toggle/Toggle.test.js | 10 +- src/form/formik/FormikEditableList/index.js | 2 +- src/form/redux-form/FieldCodeEditor/index.js | 30 +- src/form/redux-form/FieldCombobox/index.js | 30 +- src/form/redux-form/FieldCombobox2/index.js | 4 +- src/form/redux-form/FieldDatePicker/index.js | 4 +- src/form/redux-form/FieldDropdown/index.js | 24 +- .../redux-form/FieldEditableList/index.js | 30 +- src/form/redux-form/FieldFileInput/index.js | 16 +- src/form/redux-form/FieldInput/index.js | 16 +- src/form/redux-form/FieldMultiselect/index.js | 30 +- src/form/redux-form/FieldPagination/index.js | 4 +- src/form/redux-form/FieldSelect/index.js | 30 +- .../redux-form/FieldSelectableList/index.js | 20 +- src/form/redux-form/FieldToggle/index.js | 20 +- src/hooks/useTimeout/index.js | 34 +- 82 files changed, 1397 insertions(+), 1186 deletions(-) diff --git a/.gitignore b/.gitignore index d69d7ae8..bc1ecf96 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ coverage .nyc_output *DS_Store cypress/screenshots +.vscode diff --git a/package-lock.json b/package-lock.json index 1cd54e5f..aaaf3918 100644 --- a/package-lock.json +++ b/package-lock.json @@ -39,10 +39,10 @@ "css-loader": "6.8.1", "cypress": "13.6.1", "esbuild-loader": "4.0.2", - "eslint": "8.55.0", + "eslint": "8.57.0", "eslint-config-airbnb": "19.0.4", "eslint-plugin-cypress": "2.15.1", - "eslint-plugin-import": "2.29.0", + "eslint-plugin-import": "2.29.1", "eslint-plugin-jsx-a11y": "6.8.0", "eslint-plugin-react": "7.33.2", "eslint-plugin-react-hooks": "4.6.0", @@ -2542,22 +2542,22 @@ } }, "node_modules/@eslint/js": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.55.0.tgz", - "integrity": "sha512-qQfo2mxH5yVom1kacMtZZJFVdW+E70mqHMJvVg6WTLo+VBuQJ4TojZlfWBjK0ve5BdEeNAVxOsl/nvNMpJOaJA==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.13", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", - "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^2.0.1", - "debug": "^4.1.1", + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", "minimatch": "^3.0.5" }, "engines": { @@ -2578,9 +2578,9 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", - "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", + "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", "dev": true }, "node_modules/@istanbuljs/load-nyc-config": { @@ -6825,16 +6825,16 @@ } }, "node_modules/eslint": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.55.0.tgz", - "integrity": "sha512-iyUUAM0PCKj5QpwGfmCAG9XXbZCWsqP/eWAWrG/W0umvjuLRBECwSFdt+rCntju0xEH7teIABPwXpahftIaTdA==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.55.0", - "@humanwhocodes/config-array": "^0.11.13", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "@ungap/structured-clone": "^1.2.0", @@ -7005,9 +7005,9 @@ } }, "node_modules/eslint-plugin-import": { - "version": "2.29.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.0.tgz", - "integrity": "sha512-QPOO5NO6Odv5lpoTkddtutccQjysJuFxoPS7fAHO+9m9udNHvTCPSAMW9zGAYj8lAIdr40I8yPCdUYrncXtrwg==", + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", + "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", "dev": true, "dependencies": { "array-includes": "^3.1.7", @@ -7026,7 +7026,7 @@ "object.groupby": "^1.0.1", "object.values": "^1.1.7", "semver": "^6.3.1", - "tsconfig-paths": "^3.14.2" + "tsconfig-paths": "^3.15.0" }, "engines": { "node": ">=4" @@ -12536,9 +12536,9 @@ } }, "node_modules/postcss": { - "version": "8.4.32", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.32.tgz", - "integrity": "sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==", + "version": "8.4.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", + "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", "dev": true, "funding": [ { @@ -12623,9 +12623,9 @@ } }, "node_modules/postcss-selector-parser": { - "version": "6.0.13", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", - "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==", + "version": "6.0.15", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz", + "integrity": "sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==", "dev": true, "dependencies": { "cssesc": "^3.0.0", @@ -15239,9 +15239,9 @@ } }, "node_modules/tsconfig-paths": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", - "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", "dev": true, "dependencies": { "@types/json5": "^0.0.29", diff --git a/package.json b/package.json index 5f004855..0e4a40f4 100644 --- a/package.json +++ b/package.json @@ -54,10 +54,10 @@ "css-loader": "6.8.1", "cypress": "13.6.1", "esbuild-loader": "4.0.2", - "eslint": "8.55.0", + "eslint": "8.57.0", "eslint-config-airbnb": "19.0.4", "eslint-plugin-cypress": "2.15.1", - "eslint-plugin-import": "2.29.0", + "eslint-plugin-import": "2.29.1", "eslint-plugin-jsx-a11y": "6.8.0", "eslint-plugin-react": "7.33.2", "eslint-plugin-react-hooks": "4.6.0", diff --git a/src/components/Accordion/js/AccordionItem.test.js b/src/components/Accordion/js/AccordionItem.test.js index b1329e65..86f728f7 100644 --- a/src/components/Accordion/js/AccordionItem.test.js +++ b/src/components/Accordion/js/AccordionItem.test.js @@ -49,8 +49,9 @@ describe('AccordionItem', () => { .get(selectors.itemActive) .should('exist') .get(selectors.trigger) - .click() - .get(selectors.itemActive) + .click(); + + cy.get(selectors.itemActive) .should('not.exist'); }); @@ -60,8 +61,9 @@ describe('AccordionItem', () => { .get(selectors.itemActive) .should('exist') .get(selectors.trigger) - .click() - .get(selectors.itemActive) + .click(); + + cy.get(selectors.itemActive) .should('exist'); }); }); diff --git a/src/components/Accordion/js/AccordionItemTrigger.js b/src/components/Accordion/js/AccordionItemTrigger.js index 8d65c542..29e9f9bd 100644 --- a/src/components/Accordion/js/AccordionItemTrigger.js +++ b/src/components/Accordion/js/AccordionItemTrigger.js @@ -17,6 +17,7 @@ function ItemTrigger({ wrap }) { className={classes} onClick={toggleChange} type="button" + aria-label="collapse/expand" > diff --git a/src/components/Button/Button.test.js b/src/components/Button/Button.test.js index 721cd502..99b45da8 100644 --- a/src/components/Button/Button.test.js +++ b/src/components/Button/Button.test.js @@ -83,7 +83,7 @@ describe('Button', () => { }); it('should call the onClick callback', () => { - const onClick = cy.stub(); + const onClick = cy.stub().as('onClick'); cy .mount( @@ -96,12 +96,14 @@ describe('Button', () => { ); cy.contains(buttonText) - .click() - .then(() => expect(onClick).to.be.called); + .click(); + + cy.get('@onClick') + .should('be.called'); }); it('should not call the onClick callback if disabled', () => { - const onClick = cy.stub(); + const onClick = cy.stub().as('onClick'); cy .mount( @@ -115,7 +117,9 @@ describe('Button', () => { ); cy.contains(buttonText) - .click({ force: true }) - .then(() => expect(onClick).not.to.be.called); + .click({ force: true }); + + cy.get('@onClick') + .should('not.be.called'); }); }); diff --git a/src/components/Button/index.js b/src/components/Button/index.js index b39349a0..5cf09aa5 100644 --- a/src/components/Button/index.js +++ b/src/components/Button/index.js @@ -1,4 +1,4 @@ -import React, { Fragment, cloneElement } from 'react'; +import React, { cloneElement } from 'react'; import PropTypes from 'prop-types'; import Icon from '../Icon'; @@ -65,13 +65,12 @@ const Button = React.forwardRef((props, ref) => { if (customButton) { return ( - + <> { cloneElement(customButton, { className: getCssClasses(), disabled, - }) - } - + })} + ); } @@ -99,7 +98,7 @@ const Button = React.forwardRef((props, ref) => { className={getCssClasses()} disabled={disabled} onClick={onClick} - type={type || 'button'} + type={type || 'button'} // eslint-disable-line react/button-has-type ref={ref} {...rest} > @@ -113,7 +112,6 @@ const Button = React.forwardRef((props, ref) => { return buttonType; }; - return ( getButtonType() ); diff --git a/src/components/Collapsible/Collapsible.test.js b/src/components/Collapsible/Collapsible.test.js index e930b6ac..1c4def45 100644 --- a/src/components/Collapsible/Collapsible.test.js +++ b/src/components/Collapsible/Collapsible.test.js @@ -78,7 +78,6 @@ describe('Collapsible', () => { ); } - // eslint-disable-next-line cypress/no-unnecessary-waiting cy .mount() .get(wrapperSelector) @@ -87,16 +86,20 @@ describe('Collapsible', () => { .get('button') .contains('large') - .click() - .wait(200) + .click(); + + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(200) .get(wrapperSelector) .invoke('height') .should('be.gt', epsilon) .get('button') .contains('small') - .click() - .wait(200) + .click(); + + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(200) .get(wrapperSelector) .invoke('height') .should('be.lt', epsilon); diff --git a/src/components/Confirm/Confirm.test.js b/src/components/Confirm/Confirm.test.js index 5f6663dd..05b86c89 100644 --- a/src/components/Confirm/Confirm.test.js +++ b/src/components/Confirm/Confirm.test.js @@ -46,8 +46,9 @@ describe('Confirm', () => { .get(selectors.modalBackdrop) .should('not.have.class', 'opened') .get(selectors.contentButton) - .click() - .get(selectors.modalBackdrop) + .click(); + + cy.get(selectors.modalBackdrop) .should('have.class', 'opened'); }); @@ -82,45 +83,47 @@ describe('Confirm', () => { it('calls the confirmCallback when clicking on the confirm button', () => { const confirmBtnText = 'Custom Confirm'; - const confirmCallback = cy.stub(); + const confirmCallback = cy.stub().as('confirmCallback'); cy .mount() .get(selectors.contentButton) - .click() - .get('button') + .click(); + + cy.get('button') .contains(confirmBtnText) - .click() - .then(() => { - expect(confirmCallback).to.be.called; - }); + .click(); + + cy.get('@confirmCallback') + .should('be.called'); }); it('calls the cancelCallback when clicking on the confirm button', () => { const cancelBtnText = 'Custom Cancel'; - const cancelCallback = cy.stub(); + const cancelCallback = cy.stub().as('cancelCallback'); cy .mount() .get(selectors.contentButton) - .click() - .get('button') + .click(); + + cy.get('button') .contains(cancelBtnText) - .click() - .then(() => { - expect(cancelCallback).to.be.called; - }); + .click(); + + cy.get('@cancelCallback') + .should('be.called'); }); it('calls the beforeCallback when showing the modal', () => { - const beforeCallback = cy.stub(); + const beforeCallback = cy.stub().as('beforeCallback'); cy .mount() .get(selectors.contentButton) - .click() - .then(() => { - expect(beforeCallback).to.be.called; - }); + .click(); + + cy.get('@beforeCallback') + .should('be.called'); }); }); diff --git a/src/components/Confirm/index.js b/src/components/Confirm/index.js index 834e006c..d331f14d 100644 --- a/src/components/Confirm/index.js +++ b/src/components/Confirm/index.js @@ -61,8 +61,8 @@ function Confirm(props) { diff --git a/src/components/CopyToClipboard/CopyToClipboard.test.js b/src/components/CopyToClipboard/CopyToClipboard.test.js index 29277c87..16f8bd6d 100644 --- a/src/components/CopyToClipboard/CopyToClipboard.test.js +++ b/src/components/CopyToClipboard/CopyToClipboard.test.js @@ -1,11 +1,11 @@ import React from 'react'; import CopyToClipboard from './index'; -import Button from '../Button' +import Button from '../Button'; describe('CoopyToClipboard', () => { it('Test component rendering and Test copy functionality', () => { - const textToCopy = "boooo"; - const displayText = "BUTTON_TEST_NAME" + const textToCopy = 'boooo'; + const displayText = 'BUTTON_TEST_NAME'; cy.mount(
{ theme="primary" element={Button} onCopy={() => { - document.querySelector('.dummy').innerText = textToCopy + document.querySelector('.dummy').innerText = textToCopy; }} /> - PLACEHOLDER -
+ PLACEHOLDER + , ); - cy.get('span.dummy').invoke('text').should('eq', 'PLACEHOLDER'); + cy.get('span.dummy').invoke('text').should('eq', 'PLACEHOLDER'); cy.contains(textToCopy).should('not.exist'); - cy.contains(displayText).should('exist').click() - cy.get('span.dummy').invoke('text').should('eq', textToCopy); - + cy.contains(displayText).should('exist').click(); + cy.get('span.dummy').invoke('text').should('eq', textToCopy); }); -}); \ No newline at end of file +}); diff --git a/src/components/CopyToClipboard/index.js b/src/components/CopyToClipboard/index.js index c394604b..beaa3bba 100644 --- a/src/components/CopyToClipboard/index.js +++ b/src/components/CopyToClipboard/index.js @@ -1,4 +1,4 @@ -import React, { Fragment, createRef, useCallback } from 'react'; +import React, { createRef, useCallback } from 'react'; import PropTypes from 'prop-types'; import toast from '../Toast'; @@ -8,7 +8,7 @@ import toast from '../Toast'; * - Can be used normally with a text or a custom components (Eg. Button, Icon, etc) */ -const CopyToClipboard = (props) => { +function CopyToClipboard(props) { const { display, copy, children, onCopy, } = props; @@ -29,7 +29,7 @@ const CopyToClipboard = (props) => { }, [onCopy, txtRef]); return ( - + <> {children || display} @@ -38,9 +38,9 @@ const CopyToClipboard = (props) => { {copy} ) : null} - + ); -}; +} CopyToClipboard.propTypes = { /** Callback function executed after text is copied */ diff --git a/src/components/FixedWrapper/FixedWrapper.test.js b/src/components/FixedWrapper/FixedWrapper.test.js index 51943583..15da25b8 100644 --- a/src/components/FixedWrapper/FixedWrapper.test.js +++ b/src/components/FixedWrapper/FixedWrapper.test.js @@ -32,12 +32,12 @@ describe('FixedWrapper', () => { .get(selectors.scrolled) .should('not.exist'); - cy.scrollTo(0, 500) - .get(selectors.scrolled) + cy.scrollTo(0, 500); + cy.get(selectors.scrolled) .should('exist'); - cy.scrollTo(0, 0) - .get(selectors.scrolled) + cy.scrollTo(0, 0); + cy.get(selectors.scrolled) .should('not.exist'); }); }); diff --git a/src/components/InfiniteScroller/InfiniteScroller.test.js b/src/components/InfiniteScroller/InfiniteScroller.test.js index 52800123..12341371 100644 --- a/src/components/InfiniteScroller/InfiniteScroller.test.js +++ b/src/components/InfiniteScroller/InfiniteScroller.test.js @@ -42,7 +42,7 @@ describe('InfiniteScroller', () => { }); it('scrolling to the bottom loads the next page', () => { - const loadMore = cy.stub(); + const loadMore = cy.stub().as('loadMore'); const content = ( <> {[...Array(50)].map((_, index) => ( @@ -51,21 +51,21 @@ describe('InfiniteScroller', () => { ); - // eslint-disable-next-line cypress/no-unnecessary-waiting cy.mount() - .get(selectors.wrapper) - .scrollTo(0, 10) - .wait(500) - .then(() => { - expect(loadMore).not.to.be.called; - }) + .scrollTo(0, 10); - .get(selectors.wrapper) - .scrollTo('bottom') - .wait(500) - .then(() => { - expect(loadMore).to.be.called; - }); + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(500) + .get('@loadMore') + .should('not.be.called'); + + cy.get(selectors.wrapper) + .scrollTo('bottom'); + + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(500) + .get('@loadMore') + .should('be.called'); }); }); diff --git a/src/components/List/List.test.js b/src/components/List/List.test.js index 1eb2e9ae..98f5ef65 100644 --- a/src/components/List/List.test.js +++ b/src/components/List/List.test.js @@ -12,17 +12,17 @@ describe('List', () => { Bravo Charlie Delta - - ) - + , + ); + cy.get('.tyk-list__wrapper') .get('ul') - .should('have.class', 'tyk-list') + .should('have.class', 'tyk-list'); - cy.contains("LABEL").should('exist'); - cy.contains("Alpha").should('exist'); - cy.contains("Bravo").should('exist'); - cy.contains("Charlie").should('exist'); - cy.contains("Delta").should('exist'); + cy.contains('LABEL').should('exist'); + cy.contains('Alpha').should('exist'); + cy.contains('Bravo').should('exist'); + cy.contains('Charlie').should('exist'); + cy.contains('Delta').should('exist'); }); -}); \ No newline at end of file +}); diff --git a/src/components/List/index.js b/src/components/List/index.js index 4346d935..f41cbfca 100644 --- a/src/components/List/index.js +++ b/src/components/List/index.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import { fromJS } from 'immutable'; import ListItem from './js/ListItem'; -const List = (props) => { +function List(props) { const { className, children, @@ -55,7 +55,7 @@ const List = (props) => { ); -}; +} List.propTypes = { children: PropTypes.oneOfType([ @@ -76,8 +76,6 @@ List.defaultProps = { style: {}, }; - List.Item = ListItem; - export default List; diff --git a/src/components/List/js/ListItem.js b/src/components/List/js/ListItem.js index 019428dd..f8a9618e 100644 --- a/src/components/List/js/ListItem.js +++ b/src/components/List/js/ListItem.js @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; -const ListItem = (props) => { +function ListItem(props) { const { className, children, @@ -13,7 +13,7 @@ const ListItem = (props) => { {children} ); -}; +} ListItem.propTypes = { children: PropTypes.oneOfType([ @@ -23,6 +23,7 @@ ListItem.propTypes = { PropTypes.string, ]), className: PropTypes.string, + selected: PropTypes.bool, }; ListItem.defaultProps = { diff --git a/src/components/Message/Message.test.js b/src/components/Message/Message.test.js index 09cf6864..8fcdb3f7 100644 --- a/src/components/Message/Message.test.js +++ b/src/components/Message/Message.test.js @@ -45,12 +45,12 @@ describe('Message', () => { }); it('renders an icon that calls the onClose callback when clicked', () => { - const onClose = cy.stub(); + const onClose = cy.stub().as('onClose'); cy.mount() .get(selectors.closeIcon) - .click() - .then(() => { - expect(onClose).to.be.called; - }); + .click(); + + cy.get('@onClose') + .should('be.called'); }); }); diff --git a/src/components/Modal/Modal.test.js b/src/components/Modal/Modal.test.js index d1c26009..bc5c05ea 100644 --- a/src/components/Modal/Modal.test.js +++ b/src/components/Modal/Modal.test.js @@ -66,7 +66,7 @@ describe('Modal', () => { }); it('should call onClose when back drop is clicked', () => { - const onClose = cy.stub(); + const onClose = cy.stub().as('onClose'); cy.mount() .get(selectors.backDrop) .should('exist') @@ -74,25 +74,25 @@ describe('Modal', () => { .and('have.class', cssClasses.backDrop); cy.get(selectors.backDrop) - .click({ force: true }) - .then(() => { - expect(onClose).to.be.called; - }); + .click({ force: true }); + + cy.get('@onClose') + .should('be.called'); }); it('should not call onClose with disableCloseCommands', () => { - const onClose = cy.stub(); - cy.mount() + const onClose = cy.stub().as('onClose'); + cy.mount() .get(selectors.backDrop) .should('exist') .and('have.class', cssClasses.opened) .and('have.class', cssClasses.backDrop); cy.get(selectors.backDrop) - .click({ force: true }) - .then(() => { - expect(onClose).not.to.be.called; - }); + .click({ force: true }); + + cy.get('@onClose') + .should('not.be.called'); cy .get(selectors.backDrop) .should('exist') diff --git a/src/components/Panel/Panel.test.js b/src/components/Panel/Panel.test.js index 538e9e80..39b6c61e 100644 --- a/src/components/Panel/Panel.test.js +++ b/src/components/Panel/Panel.test.js @@ -99,11 +99,10 @@ describe('Panel', () => { cy.get('@panel') .find(selectors.header) - .click() - .then(() => { - cy.get(selectors.panel) - .should('have.class', cssClasses.collapsed); - }); + .click(); + + cy.get(selectors.panel) + .should('have.class', cssClasses.collapsed); }); it('should start with collapsed state when "collapsed" is set', () => { @@ -145,30 +144,29 @@ describe('Panel', () => { .get(selectors.header) .find(selectors.upArrowIcon) .should('exist') - .click() - .then(() => { - cy.get(selectors.header) - .find(selectors.downArrowIcon) - .should('exist'); - }); + .click(); + + cy.get(selectors.header) + .find(selectors.downArrowIcon) + .should('exist'); }); it('should call onToggleCollapse when collapsed state is change', () => { - const onCollapsed = cy.stub(); + const onCollapsed = cy.stub().as('onCollapsed'); cy.mount() .get(selectors.header) .as('header') - .click() - .then(() => { - expect(onCollapsed).to.be.calledWith(true); - }); + .click(); + + cy.get('@onCollapsed') + .should('be.calledWith', true); cy.get('@header') - .click() - .then(() => { - expect(onCollapsed).to.be.calledWith(false); - }); + .click(); + + cy.get('@onCollapsed') + .should('be.calledWith', false); }); it('should call set theme to success with theme="success"', () => { diff --git a/src/components/Panel/index.js b/src/components/Panel/index.js index 246ddfd9..05f3443f 100644 --- a/src/components/Panel/index.js +++ b/src/components/Panel/index.js @@ -1,14 +1,14 @@ -import React, { useState, useEffect } from 'react'; +import React, { + useCallback, useEffect, useMemo, useState, +} from 'react'; import PropTypes from 'prop-types'; import PanelBody from './js/PanelBody'; import PanelFooter from './js/PanelFooter'; import PanelHeader from './js/PanelHeader'; - import { PortalContext } from './panel-context'; - -const Panel = (props) => { +function Panel(props) { const { children, className, @@ -45,25 +45,25 @@ const Panel = (props) => { return cssClasses.join(' '); }; - const handleToggle = () => { + const handleToggle = useCallback(() => { setCollapsedState(!collapsedState); if (onToggleCollapse) { onToggleCollapse(!collapsedState); } - }; + }, [collapsedState, onToggleCollapse]); + + const context = useMemo(() => ({ + collapsable, + collapsed: collapsedState, + onToggle: handleToggle, + theme, + collapsibleIconPosition, + }), [collapsable, collapsedState, collapsibleIconPosition, theme, handleToggle]); return (
- + { (typeof children === 'function') ? children({ @@ -74,7 +74,7 @@ const Panel = (props) => {
); -}; +} Panel.propTypes = { children: PropTypes.oneOfType([ @@ -91,7 +91,6 @@ Panel.propTypes = { onToggleCollapse: PropTypes.func, }; - Panel.Body = PanelBody; Panel.Footer = PanelFooter; Panel.Header = PanelHeader; diff --git a/src/components/Panel/js/PanelFooter.js b/src/components/Panel/js/PanelFooter.js index 59d6d80d..d4db33c2 100644 --- a/src/components/Panel/js/PanelFooter.js +++ b/src/components/Panel/js/PanelFooter.js @@ -1,14 +1,14 @@ import React from 'react'; import PropTypes from 'prop-types'; -const PanelFooter = (props) => { +function PanelFooter(props) { const { children } = props; return (
{ children }
); -}; +} PanelFooter.propTypes = { children: PropTypes.oneOfType([ diff --git a/src/components/Panel/js/PanelHeader.js b/src/components/Panel/js/PanelHeader.js index afc261a0..de370fd1 100644 --- a/src/components/Panel/js/PanelHeader.js +++ b/src/components/Panel/js/PanelHeader.js @@ -4,16 +4,7 @@ import PropTypes from 'prop-types'; import Icon from '../../Icon'; import { PortalContext } from '../panel-context'; -export default class PanelHeader extends Component { - static propTypes = { - children: PropTypes.oneOfType([ - PropTypes.element, - PropTypes.node, - PropTypes.string, - ]).isRequired, - className: PropTypes.string, - } - +class PanelHeader extends Component { getCssClasses(props) { const { className } = this.props; let cssClasses = ['tyk-panel__header']; @@ -60,3 +51,14 @@ export default class PanelHeader extends Component { ); } } + +PanelHeader.propTypes = { + children: PropTypes.oneOfType([ + PropTypes.element, + PropTypes.node, + PropTypes.string, + ]).isRequired, + className: PropTypes.string, +}; + +export default PanelHeader; diff --git a/src/components/Pill/index.js b/src/components/Pill/index.js index 1fef6957..438b9eaa 100644 --- a/src/components/Pill/index.js +++ b/src/components/Pill/index.js @@ -4,7 +4,7 @@ import PropTypes from 'prop-types'; * Pill * */ -const Pill = (props) => { +function Pill(props) { const { children, className, @@ -12,7 +12,7 @@ const Pill = (props) => { } = props; const getCssClasses = () => { - let cssClasses = ['tyk-pill', ...theme.split(' ').map(t => `tyk-pill--${t}`)]; + let cssClasses = ['tyk-pill', ...theme.split(' ').map((t) => `tyk-pill--${t}`)]; if (className) { cssClasses = cssClasses.concat(className); @@ -26,7 +26,7 @@ const Pill = (props) => { {children} ); -}; +} Pill.propTypes = { children: PropTypes.oneOfType([ diff --git a/src/components/RevealPanel/js/RevealPanelService.js b/src/components/RevealPanel/js/RevealPanelService.js index cd86848b..ef4c86b8 100644 --- a/src/components/RevealPanel/js/RevealPanelService.js +++ b/src/components/RevealPanel/js/RevealPanelService.js @@ -21,11 +21,11 @@ class RevealPanelService { #onDragEndCallbacks = []; // Callback function to be executed when the panel is resized. #dragStarted = false; - /** - * Sets a callback function to be executed when the panel is resized. - * @param {function} callback - The callback function to be called on panel resize. - */ + /** + * Sets a callback function to be executed when the panel is resized. + * @param {function} callback - The callback function to be called on panel resize. + */ on(eventName, listener) { let list = null; if (eventName === 'onHeightChange') { @@ -57,11 +57,11 @@ class RevealPanelService { } /** - * Sets the panel, gutter, and wrapper elements to be controlled by this class. - * @param {HTMLElement} wrapper - The wrapper element containing the panel and gutter. - * @param {HTMLElement} panel - The panel element to be resized. - * @param {HTMLElement} gutter - The gutter element used for resizing. - */ + * Sets the panel, gutter, and wrapper elements to be controlled by this class. + * @param {HTMLElement} wrapper - The wrapper element containing the panel and gutter. + * @param {HTMLElement} panel - The panel element to be resized. + * @param {HTMLElement} gutter - The gutter element used for resizing. + */ setRefs(wrapper, panel, gutter) { if (this.#panel || this.#gutter || this.#wrapper) { this.unbindEvents(); @@ -74,8 +74,8 @@ class RevealPanelService { } /** - * Attaches event listeners for panel resizing. - */ + * Attaches event listeners for panel resizing. + */ bindEvents() { this.ensureRefs('bindEvents'); this.#gutter.addEventListener('mousedown', this.onMouseDown.bind(this)); @@ -84,9 +84,9 @@ class RevealPanelService { } /** - * Gets the current height of the panel. - * @returns {number} - The height of the panel in pixels. - */ + * Gets the current height of the panel. + * @returns {number} - The height of the panel in pixels. + */ getHeight() { this.ensureRefs('getHeight'); const size = this.#panel.getBoundingClientRect(); @@ -94,9 +94,9 @@ class RevealPanelService { } /** - * Handles the mouse down event, initiating the resizing process. - * @param {MouseEvent} e - The mouse event object. - */ + * Handles the mouse down event, initiating the resizing process. + * @param {MouseEvent} e - The mouse event object. + */ onMouseDown(e) { this.ensureRefs('onMouseDown'); this.#state = { @@ -107,9 +107,9 @@ class RevealPanelService { } /** - * Handles the mouse move event, adjusting the panel's height during resizing. - * @param {MouseEvent} e - The mouse event object. - */ + * Handles the mouse move event, adjusting the panel's height during resizing. + * @param {MouseEvent} e - The mouse event object. + */ onMouseMove(e) { this.ensureRefs('onMouseMove'); @@ -181,8 +181,8 @@ class RevealPanelService { } /** - * Resets the state after the mouse up event, ending the resizing process. - */ + * Resets the state after the mouse up event, ending the resizing process. + */ onMouseUp() { this.#state = null; this.onDragEnd.bind(this)(); @@ -190,9 +190,9 @@ class RevealPanelService { } /** - * Sets the height of the panel. - * @param {string} height - The new height value as a CSS string (e.g., "100px"). - */ + * Sets the height of the panel. + * @param {string} height - The new height value as a CSS string (e.g., "100px"). + */ setHeight(height) { let heightVal = height; this.ensureRefs('setHeight'); @@ -207,9 +207,9 @@ class RevealPanelService { } /** - * Ensures that the required elements (wrapper, panel, and gutter) are set. - * Throws an error if they are missing. - */ + * Ensures that the required elements (wrapper, panel, and gutter) are set. + * Throws an error if they are missing. + */ ensureRefs(reason) { if (!this.#panel || !this.#gutter || !this.#wrapper) { throw new Error( @@ -219,33 +219,33 @@ class RevealPanelService { } /** - * Checks if the panel is hidden. - * @returns {boolean} - True if the panel is hidden; false otherwise. - */ + * Checks if the panel is hidden. + * @returns {boolean} - True if the panel is hidden; false otherwise. + */ isHidden() { this.ensureRefs('isHidden'); return this.#wrapper.style.visibility === 'hidden'; } /** - * Hides the panel by setting its visibility to "hidden". - */ + * Hides the panel by setting its visibility to "hidden". + */ hide() { this.ensureRefs('hide'); this.#wrapper.style.visibility = 'hidden'; } /** - * Shows the panel by setting its visibility to "visible". - */ + * Shows the panel by setting its visibility to "visible". + */ show() { this.ensureRefs('show'); this.#wrapper.style.visibility = 'visible'; } /** - * Unbinds the event listeners added for resizing. - */ + * Unbinds the event listeners added for resizing. + */ unbindEvents() { this.#onHeightChangeCallbacks = []; this.#onDragStartCallbacks = []; diff --git a/src/components/Table/Table.test.js b/src/components/Table/Table.test.js index af44cb96..2a8325ed 100644 --- a/src/components/Table/Table.test.js +++ b/src/components/Table/Table.test.js @@ -72,10 +72,16 @@ describe('Table Component Rendering', () => { }; const config = { columns: [ - { id: 'name', name: 'Policy Name', type: 'string', sortable: { default: 'DESC' } }, + { + id: 'name', name: 'Policy Name', type: 'string', sortable: { default: 'DESC' }, + }, { id: 'access_rights', name: 'Access Rights', type: 'string' }, - { id: 'auth_type', name: 'Auth Type', type: 'string', sortable: true }, - { id: 'date', name: 'Date Created', type: 'string', sortable: true }, + { + id: 'auth_type', name: 'Auth Type', type: 'string', sortable: true, + }, + { + id: 'date', name: 'Date Created', type: 'string', sortable: true, + }, ], rows: [ { @@ -127,12 +133,16 @@ describe('Table Component Rendering', () => { }); it('should render all column headers as per the configuration', () => { - //because it's selectable, we have an extra column + // because it's selectable, we have an extra column cy - .mount() + .mount( +
, + ) .get(selectors.headerCell).should('have.length', config.columns.length + 1); config.columns.forEach((column) => { @@ -144,10 +154,14 @@ describe('Table Component Rendering', () => { it('should display a row with checkboxes if the table is set as selectable', () => { cy - .mount(
) + .mount( +
, + ) .get(selectors.selectAllCheckbox) .should('have.length', 3); }); @@ -178,37 +192,43 @@ describe('Table Component Rendering', () => { .mount(
) .get(selectors.table).should('not.exist'); }); + it('should render a loading indicator when loading is true', () => { cy .mount(
) .get(selectors.loadingIndicator).should('exist'); }); + it('should render the table with infinite scrolling enabled', () => { cy.mount(
); cy.get(selectors.table).should('exist'); cy.get(selectors.pagination).should('not.exist'); cy.get(selectors.infiniteScroll).should('exist'); }); + it('should render sortable columns with column headers', () => { cy.mount(
) .get(selectors.table).should('exist'); cy.get(selectors.sortIcon).should('have.length', 3); }); + it('should call the onChange callback with message sort', () => { - const callbacks = { - onChange: (values) => {console.log('result', values);} - }; const onChange = cy.stub().as('onChange'); cy.mount(
); cy.get(`${selectors.columnHeader}:first ${selectors.sortIconClassName}`).click(); cy.get('@onChange').should('be.calledWith', 'sort'); }); + it('should allow selecting and deselecting a single row', () => { cy.spy(config.rows[0].events, 'onClick').as('onClickSpy'); - cy.mount(
); + cy.mount( +
, + ); cy.get(`${selectors.row}:eq(0)`).find(selectors.checkbox).check(); cy.get('@onClickSpy').should('be.called'); }); diff --git a/src/components/Table/index.js b/src/components/Table/index.js index 992de41a..b8509c87 100644 --- a/src/components/Table/index.js +++ b/src/components/Table/index.js @@ -1,4 +1,6 @@ -import React, { useState, useEffect, useRef } from 'react'; +import React, { + useCallback, useEffect, useMemo, useRef, useState, +} from 'react'; import PropTypes from 'prop-types'; import Message from '../Message'; import Pagination from '../../form/components/Pagination'; @@ -19,53 +21,50 @@ function Table({ const [state, setState] = useState(null); const [onChangeMsg, setOnChangeMsg] = useState('api'); const itemsListRef = useRef(null); - const sortRows = (col, sortOrder) => { - setState({ - ...state, + const sortRows = useCallback((col, sortOrder) => { + setState((prevState) => ({ + ...prevState, sort: { order: sortOrder, col, }, - }); - }; - - const selectAllRows = (data) => { - setState({ - ...state, - rows: state.rows.map((row) => ({ ...row, selected: data })), - }); - }; - - const selectRow = (data) => { - const { index, selected } = data; - const selectedRow = state.rows[index]; - - setState({ - ...state, + })); + }, []); + + const selectAllRows = useCallback((data) => { + setState((prevState) => ({ + ...prevState, + rows: prevState.rows.map((row) => ({ ...row, selected: data })), + })); + }, []); + + const selectRow = useCallback(({ index, selected }) => { + setState((prevState) => ({ + ...prevState, rows: [ - ...state.rows.slice(0, index), - { ...selectedRow, selected }, - ...state.rows.slice(index + 1), + ...prevState.rows.slice(0, index), + { ...prevState.rows[index], selected }, + ...prevState.rows.slice(index + 1), ], - }); - }; + })); + }, []); - const setPagination = (data) => { - setState({ - ...state, + const setPagination = useCallback((data) => { + setState((prevState) => ({ + ...prevState, pagination: { ...state.pagination, current: data + 1, }, - }); - }; + })); + }, []); const api = { getState: () => state, setState: (newState) => setState(newState), }; - const sendMessage = (message, data) => { + const sendMessage = useCallback((message, data) => { setOnChangeMsg(message); if (message === 'sort') { sortRows(data.column.id, data.sortOrder); @@ -86,7 +85,9 @@ function Table({ if (message === 'pagination.change') { setPagination(data); } - }; + }, [sortRows, selectAllRows, selectRow, setPagination]); + + const context = useMemo(() => ({ state, sendMessage }), [state, sendMessage]); useEffect(() => setState(value), [value]); useEffect(() => { @@ -111,9 +112,8 @@ function Table({
); - return ( - +
{ loading && diff --git a/src/components/Table/js/body.js b/src/components/Table/js/body.js index e7121d95..a7d63565 100644 --- a/src/components/Table/js/body.js +++ b/src/components/Table/js/body.js @@ -2,8 +2,7 @@ import React, { useContext } from 'react'; import { tableContext } from '../tableContext'; import { Row } from './row'; - -export const Body = () => { +export function Body() { const { state } = useContext(tableContext); const { rows } = state; @@ -15,4 +14,4 @@ export const Body = () => { } ); -}; +} diff --git a/src/components/Table/js/cell.js b/src/components/Table/js/cell.js index c2b466f2..8892634a 100644 --- a/src/components/Table/js/cell.js +++ b/src/components/Table/js/cell.js @@ -1,9 +1,9 @@ import React from 'react'; import PropTypes from 'prop-types'; -export const Cell = ({ col, row }) => { +export function Cell({ col, row }) { if (!row.values[col.id]) { - return ; + return ; // eslint-disable-line jsx-a11y/control-has-associated-label } if (col.type === 'string') { @@ -30,7 +30,7 @@ export const Cell = ({ col, row }) => { ); -}; +} Cell.propTypes = { col: PropTypes.instanceOf(Object), diff --git a/src/components/Table/js/header-cell.js b/src/components/Table/js/header-cell.js index 2b273fea..cb080f2e 100644 --- a/src/components/Table/js/header-cell.js +++ b/src/components/Table/js/header-cell.js @@ -4,7 +4,7 @@ import PropTypes from 'prop-types'; import { tableContext } from '../tableContext'; import Icon from '../../Icon'; -export const HeaderCell = ({ column }) => { +export function HeaderCell({ column }) { const { sortable } = column; const [sortOrder, setSortOrder] = useState(sortable?.default || 'ASC'); const { state, sendMessage } = useContext(tableContext); @@ -25,7 +25,7 @@ export const HeaderCell = ({ column }) => { )} ); -}; +} HeaderCell.propTypes = { column: PropTypes.instanceOf(Object), diff --git a/src/components/Table/js/header.js b/src/components/Table/js/header.js index b0017397..01651751 100644 --- a/src/components/Table/js/header.js +++ b/src/components/Table/js/header.js @@ -2,12 +2,12 @@ import React, { useContext, useState } from 'react'; import { HeaderCell } from './header-cell'; import { tableContext } from '../tableContext'; -export const Header = () => { +export function Header() { const [selectAll, setSelectAll] = useState(false); const { state, sendMessage } = useContext(tableContext); const { columns, selectable } = state; - const generateHeaders = () => columns.map(column => ( + const generateHeaders = () => columns.map((column) => ( { ); -}; +} diff --git a/src/components/Table/js/row.js b/src/components/Table/js/row.js index d7bb0c3f..489e830a 100644 --- a/src/components/Table/js/row.js +++ b/src/components/Table/js/row.js @@ -1,21 +1,22 @@ -import React, { useContext } from 'react'; +import React, { useCallback, useContext } from 'react'; import PropTypes from 'prop-types'; import Checkbox from '../../../form/components/Checkbox'; import { Cell } from './cell'; import { tableContext } from '../tableContext'; -export const Row = ({ row, index }) => { +export function Row({ row, index }) { const { state, sendMessage } = useContext(tableContext); const { columns, selectable } = state; - const SelectableCheckbox = () => ( + const SelectableCheckbox = useCallback(() => ( + // eslint-disable-next-line jsx-a11y/control-has-associated-label sendMessage('row.selected', { index, selected: v })} + onChange={(v) => sendMessage('row.selected', { index, selected: v })} /> - ); + ), []); return ( { className={row.styling?.className} > {selectable && selectable.position === 'LEFT' && SelectableCheckbox()} - {columns.map(col => ( + {columns.map((col) => ( ))} {selectable && selectable.position === 'RIGHT' && SelectableCheckbox()} ); -}; +} Row.propTypes = { row: PropTypes.instanceOf(Object), diff --git a/src/components/Tabs/Tabs.test.js b/src/components/Tabs/Tabs.test.js index 93527fad..8577afef 100644 --- a/src/components/Tabs/Tabs.test.js +++ b/src/components/Tabs/Tabs.test.js @@ -100,7 +100,7 @@ describe('Tabs', () => { }); it('should call handler function on tab change and add active classes', () => { - const onTabChange = cy.stub(); + const onTabChange = cy.stub().as('onTabChange'); cy.mount() .get(selectors.tabs) @@ -113,19 +113,19 @@ describe('Tabs', () => { .as('tabHeader') .find(selectors.button) .should('exist') - .click() - .then(() => { - cy.get('@tabs') - .should('exist') - .find(selectors.active) - .should('exist') - .and('have.length', 1); - - cy.get('@tabHeader') - .should('have.class', classNames.active); - - expect(onTabChange).to.be.called; - }); + .click(); + + cy.get('@tabs') + .should('exist') + .find(selectors.active) + .should('exist') + .and('have.length', 1); + + cy.get('@tabHeader') + .should('have.class', classNames.active); + + cy.get('@onTabChange') + .should('be.called'); }); it('should support different themes when type prop is supplied', () => { @@ -172,30 +172,26 @@ describe('Tabs', () => { .find(selectors.button) .should('exist') .and('have.length', 1) - .click() - .then(() => { - cy.get('@tabs') - .find('#hide') - .should('exist') - .click() - .then(() => { - cy.get('@tabs') - .find(selectors.tabHeaderContainer) - .find(selectors.tabHeader) - .should('have.length', 1); - }) - .then(() => { - cy.get('@tabs') - .find('#show') - .should('exist') - .click() - .then(() => { - cy.get('@tabs') - .find(selectors.tabHeaderContainer) - .find(selectors.tabHeader) - .should('have.length', 2); - }); - }); - }); + .click(); + + cy.get('@tabs') + .find('#hide') + .should('exist') + .click(); + + cy.get('@tabs') + .find(selectors.tabHeaderContainer) + .find(selectors.tabHeader) + .should('have.length', 1); + + cy.get('@tabs') + .find('#show') + .should('exist') + .click(); + + cy.get('@tabs') + .find(selectors.tabHeaderContainer) + .find(selectors.tabHeader) + .should('have.length', 2); }); }); diff --git a/src/components/Tabs/index.js b/src/components/Tabs/index.js index 890748c3..f7b97b11 100644 --- a/src/components/Tabs/index.js +++ b/src/components/Tabs/index.js @@ -1,6 +1,5 @@ - import React, { - useEffect, useState, + useEffect, useMemo, useState, } from 'react'; import PropTypes from 'prop-types'; import Tab from './js/Tab'; @@ -9,7 +8,7 @@ import Collapsible from '../Collapsible'; import Icon from '../Icon'; import { TabsContext } from './tabs-context'; -const Tabs = (props) => { +function Tabs(props) { const { children, className, @@ -28,8 +27,8 @@ const Tabs = (props) => { }, [initialPath]); const updateTabsList = (tabsState, path, tabData) => { - let tempTabsState = Object.assign({}, tabsState); - const tempTabData = Object.assign({}, tabData); + let tempTabsState = { ...tabsState }; + const tempTabData = { ...tabData }; const tempPath = path.slice(0); while (tempPath.length > 1) { @@ -47,7 +46,7 @@ const Tabs = (props) => { }; const toggleTab = (tabsState, path) => { - let tempTabsState = Object.assign({}, tabsState); + let tempTabsState = { ...tabsState }; const tempPath = path.slice(0); while (tempPath.length > 1) { @@ -86,7 +85,7 @@ const Tabs = (props) => { } setTabs((prevTabs) => { - let tempTabs = Object.assign({}, prevTabs); + let tempTabs = { ...prevTabs }; tempTabs = toggleTab(tempTabs, path); @@ -161,7 +160,7 @@ const Tabs = (props) => { }; const addTab = (tabData, path) => { - setTabs(prevTabs => updateTabsList({ ...prevTabs }, path, tabData)); + setTabs((prevTabs) => updateTabsList({ ...prevTabs }, path, tabData)); if (tabData.selected) { setInitialPath(path); @@ -211,23 +210,34 @@ const Tabs = (props) => { return !!(tempTabs && tempTabs[key]); }; + const context = useMemo(() => ({ + id, + addTab, + hideTab, + updateTab, + showTab, + tabExists, + selectedPath, + rendered, + hideTabContent, + tabs, + }), [ + id, + addTab, + hideTab, + updateTab, + showTab, + tabExists, + selectedPath, + rendered, + hideTabContent, + tabs, + ]); + return (
{genTabs(tabs)} - + { (typeof children === 'function') ? children({ @@ -239,7 +249,7 @@ const Tabs = (props) => {
); -}; +} Tabs.propTypes = { className: PropTypes.string, @@ -255,7 +265,6 @@ Tabs.propTypes = { type: PropTypes.string, }; - Tabs.Tab = Tab; export default Tabs; diff --git a/src/components/Tabs/js/Tab.js b/src/components/Tabs/js/Tab.js index 4874508f..ec024db8 100644 --- a/src/components/Tabs/js/Tab.js +++ b/src/components/Tabs/js/Tab.js @@ -1,12 +1,12 @@ import React, { - Fragment, useContext, useEffect, useState, + useContext, useEffect, useState, } from 'react'; import PropTypes from 'prop-types'; import { TabsContext } from '../tabs-context'; import TabContext from './TabContext'; import TabContent from './TabContent'; -const Tab = (props) => { +function Tab(props) { const { id } = props; const tabsContext = useContext(TabsContext); const tabContext = useContext(TabContext); @@ -45,26 +45,24 @@ const Tab = (props) => { } return ( - - - {children} - - + + {children} + ); -}; +} Tab.propTypes = { children: PropTypes.oneOfType([ diff --git a/src/components/Tabs/js/TabContent.js b/src/components/Tabs/js/TabContent.js index 02b69e3b..0475903e 100644 --- a/src/components/Tabs/js/TabContent.js +++ b/src/components/Tabs/js/TabContent.js @@ -4,7 +4,7 @@ import PropTypes from 'prop-types'; import TabContext from './TabContext'; import { usePrevious } from '../../../hooks'; -const TabContent = (props) => { +function TabContent(props) { const { addTab, hideTab, @@ -60,17 +60,14 @@ const TabContent = (props) => { return selectedPath && selectedPath.indexOf(tabData.id) > -1; }, [hideTabContent, rendered, selectedPath, tabData, tabs]); + const context = useMemo(() => ({ path, tabsId }), [path, tabsId]); + return ( ( shouldRender ? (
-1 ? 'block' : 'none' }}> - + {children}
@@ -78,7 +75,7 @@ const TabContent = (props) => { : null ) ); -}; +} TabContent.propTypes = { addTab: PropTypes.func, diff --git a/src/components/TextEllipsis/TextEllipsis.test.js b/src/components/TextEllipsis/TextEllipsis.test.js index a36fd1c5..67cf44c6 100644 --- a/src/components/TextEllipsis/TextEllipsis.test.js +++ b/src/components/TextEllipsis/TextEllipsis.test.js @@ -26,8 +26,9 @@ describe('TextEllipsis', () => { cy.get(`.${classes.floatingContainer}`) .should('not.exist') .get('.text-ellipsis') - .trigger('mouseover') - .get(`.${classes.floatingContainer}`) + .trigger('mouseover'); + + cy.get(`.${classes.floatingContainer}`) .should('exist'); }); }); diff --git a/src/components/Toast/Toast.test.js b/src/components/Toast/Toast.test.js index b2550fd6..399b1e04 100644 --- a/src/components/Toast/Toast.test.js +++ b/src/components/Toast/Toast.test.js @@ -58,9 +58,12 @@ describe('Toast', () => { it('should render toast components and remove it after the given delay', () => { cy.mount( toast.notify('demo', { delay: 150 })} />) .get('button') - .click() - .click() - .get(selectors.toastContainer) + .click(); + + cy.get('button') + .click(); + + cy.get(selectors.toastContainer) .should('exist') .find(selectors.message) .should('exist') @@ -77,8 +80,9 @@ describe('Toast', () => { const onClick = () => toast[theme].bind(toast)(`demo ${theme}`, { timeout: 200 }); cy.mount() .get('button') - .click() - .get(selectors.toastContainer) + .click(); + + cy.get(selectors.toastContainer) .should('exist') .find(`.${classNames.message}.${className}`) .should('exist') diff --git a/src/components/Toast/js/ToastContainer.js b/src/components/Toast/js/ToastContainer.js index 0fd2f28d..b0311a63 100644 --- a/src/components/Toast/js/ToastContainer.js +++ b/src/components/Toast/js/ToastContainer.js @@ -5,7 +5,7 @@ import PropTypes from 'prop-types'; import ToastMessage from './ToastMessage'; -const ToastContainer = (props) => { +function ToastContainer(props) { const [messages, setMessages] = useState({}); const { notify, @@ -13,13 +13,13 @@ const ToastContainer = (props) => { const updateNotifications = (message, options) => { const msgID = Math.floor(Math.random() * 1000000); - + setMessages((prevMessages) => ({ ...prevMessages, [msgID]: { message, options, - } + }, })); }; @@ -28,7 +28,7 @@ const ToastContainer = (props) => { }, [messages]); const onMessageClosed = (index) => { - const tempMessages = {...messages}; + const tempMessages = { ...messages }; delete tempMessages[index]; setMessages((prevMessages) => { const { [index]: messageToBeRemoved, ...restMessages } = prevMessages; @@ -58,7 +58,7 @@ const ToastContainer = (props) => { }
); -}; +} ToastContainer.propTypes = { notify: PropTypes.func, diff --git a/src/components/Toast/js/ToastMessage.js b/src/components/Toast/js/ToastMessage.js index 8623bd5c..4ec78900 100644 --- a/src/components/Toast/js/ToastMessage.js +++ b/src/components/Toast/js/ToastMessage.js @@ -4,17 +4,18 @@ import PropTypes from 'prop-types'; import Message from '../../Message'; import { useTimeout } from '../../../hooks'; -const ToastMessage = (props) => { +function ToastMessage(props) { const { children, options, onClose, index, } = props; - const opts = Object.assign({ + const opts = { theme: 'success', delay: 3000, - }, options); + ...options, + }; let timer; const handleClose = (itemIndex) => { @@ -33,7 +34,7 @@ const ToastMessage = (props) => { {children} ); -}; +} ToastMessage.propTypes = { children: PropTypes.oneOfType([ diff --git a/src/components/Tooltip/Tooltip.test.js b/src/components/Tooltip/Tooltip.test.js index 2ca7c35c..3c87f5f3 100644 --- a/src/components/Tooltip/Tooltip.test.js +++ b/src/components/Tooltip/Tooltip.test.js @@ -28,8 +28,9 @@ describe('Tooltip', () => { cy.get(`.${classes.floatingContainer}`) .should('not.exist') .get(`.${classes.content}`) - .trigger('mouseover') - .get(`.${classes.floatingContainer}`) + .trigger('mouseover'); + + cy.get(`.${classes.floatingContainer}`) .should('exist') .get(`.${classes.tooltip}`) .should('have.class', customClass) @@ -41,8 +42,10 @@ describe('Tooltip', () => { it('can be positioned top, bottom, left, right', () => { function getContentOffset(dim) { - return cy.get(`.${classes.content}`) // eslint-disable-line cypress/no-unnecessary-waiting - .trigger('mouseover') + cy.get(`.${classes.content}`) + .trigger('mouseover'); + + return cy // eslint-disable-line cypress/no-unnecessary-waiting .get(`.${classes.floatingContainer}`) .wait(500) .invoke('offset') @@ -76,15 +79,17 @@ describe('Tooltip', () => { it('can display a "help" icon or a custom icon', () => { cy.mount(); cy.get(`.${classes.content}`) - .trigger('mouseover') - .get(`.${classes.tooltip}`) + .trigger('mouseover'); + + cy.get(`.${classes.tooltip}`) .find(`.${classes.iconHelp}`) .should('exist'); cy.mount(} />); cy.get(`.${classes.content}`) - .trigger('mouseover') - .get(`.${classes.tooltip} .${classes.iconHelp}`) + .trigger('mouseover'); + + cy.get(`.${classes.tooltip} .${classes.iconHelp}`) .should('not.exist') .get(`.${classes.tooltip} .tykon-check`) .should('exist'); @@ -93,12 +98,14 @@ describe('Tooltip', () => { it('can be triggered by clicking', () => { cy.mount(); cy.get(`.${classes.content}`) - .click() - .get(`.${classes.tooltip}`) + .click(); + + cy.get(`.${classes.tooltip}`) .should('exist') .get(`.${classes.iconClose}`) - .click() - .get(`.${classes.tooltip}`) + .click(); + + cy.get(`.${classes.tooltip}`) .should('not.exist'); }); diff --git a/src/form/components/Checkbox/Checkbox.test.js b/src/form/components/Checkbox/Checkbox.test.js index fc4c0365..93fe2c3e 100644 --- a/src/form/components/Checkbox/Checkbox.test.js +++ b/src/form/components/Checkbox/Checkbox.test.js @@ -1,33 +1,34 @@ -import React from "react"; +import React from 'react'; import Checkbox from './index'; describe('Checkbox', () => { - it('Checkbox : with error', () => { - const label = "This is a checkbox"; - const error = "Please fix the error" - cy.mount() + const label = 'This is a checkbox'; + const error = 'Please fix the error'; + cy.mount( + , + ); - cy.contains(label).should('exist') - cy.contains(error).should('exist') + cy.contains(label).should('exist'); + cy.contains(error).should('exist'); cy.get('#testCheckbox').should('not.be.checked'); cy.contains(label).should('exist').click(); cy.get('#testCheckbox').should('be.checked'); }); it('Checkbox : with disabled checked', () => { - const label = "This is a checkbox"; + const label = 'This is a checkbox'; cy.mount() + />); - cy.contains(label).should('exist') + cy.contains(label).should('exist'); cy.get('#testCheckbox').should('not.be.checked'); cy.contains(label).should('exist').click(); cy.get('#testCheckbox').should('not.be.checked'); diff --git a/src/form/components/Checkbox/index.js b/src/form/components/Checkbox/index.js index da7efcb1..bc4d7cbe 100644 --- a/src/form/components/Checkbox/index.js +++ b/src/form/components/Checkbox/index.js @@ -1,56 +1,12 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; - /** * - Checkboxes are used when a user needs to select one or more values from a series of options. * - They can be also be used as a binary toggle but consider radio instead. * - Can be used with Formik respectively (FormikCheckbox) */ -export default class Checkbox extends Component { - static propTypes = { - /** Disable a checkbox */ - disabled: PropTypes.bool, - /** Readonly prop behaves the same as disable on checkboxes */ - readOnly: PropTypes.bool, - /** Align checkbox with in same line with other elements */ - inline: PropTypes.bool, - /** Set a theme for checkbox */ - theme: PropTypes.string, - /** Set an error state for checkbox if boolean will show just icon without message */ - error: PropTypes.oneOfType([ - PropTypes.string, - PropTypes.bool, - ]), - /** Adds additional information under the checkbox element */ - note: PropTypes.string, - input: PropTypes.instanceOf(Object), - /** Adds a label to checkbox */ - label: PropTypes.oneOfType([ - PropTypes.arrayOf(PropTypes.node), - PropTypes.node, - PropTypes.element, - PropTypes.func, - PropTypes.string, - ]), - /** Set a name for checkbox */ - name: PropTypes.string, - /** Set initial value for checkbox */ - value: PropTypes.oneOfType([ - PropTypes.string, - PropTypes.bool, - ]), - /** Make checkbox checked by default */ - checked: PropTypes.oneOfType([ - PropTypes.string, - PropTypes.bool, - ]), - /** Callback function executed on change of checkbox */ - onChange: PropTypes.func, - /** CSS classes added to the wrapper of the component */ - wrapperClassName: PropTypes.string, - }; - +class Checkbox extends Component { getCssClasses() { const { inline, disabled, readOnly, theme = '', wrapperClassName = '', @@ -65,7 +21,7 @@ export default class Checkbox extends Component { } if (theme.trim()) { - cssClasses.push(...theme.split(' ').map(t => `tyk-checkbox--theme-${t}`)); + cssClasses.push(...theme.split(' ').map((t) => `tyk-checkbox--theme-${t}`)); } if (disabled || readOnly) { @@ -122,3 +78,48 @@ export default class Checkbox extends Component { ); } } + +Checkbox.propTypes = { + /** Disable a checkbox */ + disabled: PropTypes.bool, + /** Readonly prop behaves the same as disable on checkboxes */ + readOnly: PropTypes.bool, + /** Align checkbox with in same line with other elements */ + inline: PropTypes.bool, + /** Set a theme for checkbox */ + theme: PropTypes.string, + /** Set an error state for checkbox if boolean will show just icon without message */ + error: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.bool, + ]), + /** Adds additional information under the checkbox element */ + note: PropTypes.string, + input: PropTypes.instanceOf(Object), + /** Adds a label to checkbox */ + label: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.node), + PropTypes.node, + PropTypes.element, + PropTypes.func, + PropTypes.string, + ]), + /** Set a name for checkbox */ + name: PropTypes.string, + /** Set initial value for checkbox */ + value: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.bool, + ]), + /** Make checkbox checked by default */ + checked: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.bool, + ]), + /** Callback function executed on change of checkbox */ + onChange: PropTypes.func, + /** CSS classes added to the wrapper of the component */ + wrapperClassName: PropTypes.string, +}; + +export default Checkbox; diff --git a/src/form/components/CodeEditor/CodeEditor.test.js b/src/form/components/CodeEditor/CodeEditor.test.js index 264c827d..3321bea6 100644 --- a/src/form/components/CodeEditor/CodeEditor.test.js +++ b/src/form/components/CodeEditor/CodeEditor.test.js @@ -43,22 +43,34 @@ describe('CodeEditor', () => { .get(selectors.component) .should('exist') .get(selectors.editorContent) - .click() - .type(text) + .click(); + + cy.get(selectors.editorContent) + .type(text); + + cy.get(selectors.editorContent) .should('have.text', text); }); it('you cannot type if disabled or readOnly', () => { cy.mount() .get(selectors.editorContent) - .click() - .type('foo') + .click(); + + cy.get(selectors.editorContent) + .type('foo'); + + cy.get(selectors.editorContent) .should('have.text', ''); cy.mount() .get(selectors.editorContent) - .click() - .type('foo') + .click(); + + cy.get(selectors.editorContent) + .type('foo'); + + cy.get(selectors.editorContent) .should('have.text', ''); }); @@ -107,11 +119,15 @@ describe('CodeEditor', () => { const onBlur = cy.stub().as('onBlur'); cy.mount() .get(selectors.editorContent) - .click() - .type('something') - .get('.ace_text-input') - .blur() - .get('@onBlur') + .click(); + + cy.get(selectors.editorContent) + .type('something'); + + cy.get('.ace_text-input') + .blur(); + + cy.get('@onBlur') .should('be.called'); }); @@ -120,11 +136,15 @@ describe('CodeEditor', () => { const text = 'something'; cy.mount() .get(selectors.editorContent) - .click() - .type(text) - .get('.ace_text-input') - .blur() - .get('@onChange') + .click(); + + cy.get(selectors.editorContent) + .type(text); + + cy.get('.ace_text-input') + .blur(); + + cy.get('@onChange') .should('be.calledWith', text); }); diff --git a/src/form/components/Combobox/index.js b/src/form/components/Combobox/index.js index acda577b..2502b9a0 100644 --- a/src/form/components/Combobox/index.js +++ b/src/form/components/Combobox/index.js @@ -13,7 +13,7 @@ const getStateSelectedValues = (multiple, tags, value) => { return value; }; -export default class Combobox extends Component { +class Combobox extends Component { static closeList() { return { cursor: -1, @@ -31,44 +31,6 @@ export default class Combobox extends Component { return itemValue.name.toLowerCase().indexOf(inputValue) > -1; } - static propTypes = { - allowCustomValues: PropTypes.bool, - CustomListComponent: PropTypes.elementType, - searchItem: PropTypes.func, - disabled: PropTypes.bool, - error: PropTypes.string, - id: PropTypes.string, - label: PropTypes.oneOfType([ - PropTypes.arrayOf(PropTypes.node), - PropTypes.node, - PropTypes.element, - PropTypes.func, - PropTypes.string, - ]), - labelwidth: PropTypes.string, - multiple: PropTypes.bool, - max: PropTypes.number, - note: PropTypes.oneOfType([ - PropTypes.node, - PropTypes.element, - PropTypes.string, - ]), - onChange: PropTypes.func, - placeholder: PropTypes.string, - tags: PropTypes.bool, - theme: PropTypes.string, - value: PropTypes.oneOfType([ - PropTypes.string, - PropTypes.instanceOf(Object), - PropTypes.instanceOf(Array), - ]), - values: PropTypes.instanceOf(Array), - }; - - static defaultProps = { - allowCustomValues: true, - }; - constructor(props) { super(props); const { @@ -136,23 +98,116 @@ export default class Combobox extends Component { return null; } + handleItemsNavigation(e) { + const { tags } = this.props; + if (['ArrowDown', 'ArrowUp'].indexOf(e.key) === -1) { + return; + } + + const { cursor } = this.state; + const filteredValues = this.filterValues(); + let cursorNext; + + e.preventDefault(); + + if (e.key === 'ArrowDown') { + if (cursor === -1 || cursor === filteredValues.length - 1) { + cursorNext = 0; + } else if (cursor < filteredValues.length - 1) { + cursorNext = cursor + 1; + } + } + + if (e.key === 'ArrowUp') { + if (cursor > 0) { + cursorNext = cursor - 1; + } else { + cursorNext = filteredValues.length - 1; + } + } + + if (this.valuesListRef.current && cursorNext > 4) { + const scrollTop = (cursorNext - 4) * 38; + + this.valuesListRef.current.scrollTop = (!tags) ? 60 + scrollTop : scrollTop; + } else if (this.valuesListRef.current) { + this.valuesListRef.current.scrollTop = 0; + } + + this.setState((previousState) => ({ + ...previousState, + opened: true, + cursor: cursorNext, + })); + } + + handleListItemClick(index) { + // eslint-disable-next-line react/destructuring-assignment + const clickedValue = this.props.values[index]; + if (clickedValue.disabled) return; + + const { multiple, tags } = this.props; + + const methodName = (tags) ? 'manageSelectedTags' : 'manageSelectedValues'; + const tempState = { ...this.state, ...this[methodName](index) }; + + this.setState((previousState) => { + if (!multiple && !tags) { + tempState.opened = false; + } + + return { ...previousState, ...tempState }; + }); + } + + handlePillRemoveClick(index) { + const { disabled } = this.props; + + if (disabled) { + return; + } + + const tempState = this.removeSelectedValue(index); + + this.setState((previousState) => ({ ...previousState, ...tempState })); + } + + handleClickOutside(event) { + if ( + this.valuesListRef.current && !this.valuesListRef.current.contains(event.target) + && this.comboboxRef.current && !this.comboboxRef.current.contains(event.target) + ) { + this.setState((previousState) => ({ ...previousState, ...Combobox.closeList() })); + } + } + + handleComboboxDropdownClick() { + const { disabled } = this.props; + + if (disabled) { + return; + } + + this.setState((previousState) => ({ ...previousState, ...Combobox.openList() })); + } + onKeyUp(e) { const { tags, allowCustomValues } = this.props; const { cursor, opened } = this.state; const filteredValues = this.filterValues(); - let tempState = Object.assign({}, this.getSearchText()); + let tempState = this.getSearchText(); if (tags && e.key !== 'Escape') { - tempState = Object.assign({}, tempState, this.setInputWidth()); + tempState = { ...tempState, ...this.setInputWidth() }; } if (e.key === 'Enter') { const methodName = (tags) ? 'manageSelectedTags' : 'manageSelectedValues'; - tempState = Object.assign({}, tempState, this[methodName](cursor)); + tempState = { ...tempState, ...this[methodName](cursor) }; } if (allowCustomValues && !e.key === ' ' && tags) { - tempState = Object.assign({}, tempState, this.manageSelectedTags()); + tempState = { ...tempState, ...this.manageSelectedTags() }; } if ( @@ -162,18 +217,18 @@ export default class Combobox extends Component { && filteredValues.length && e.key !== 'Escape' ) { - tempState = Object.assign({}, tempState, Combobox.openList()); + tempState = { ...tempState, ...Combobox.openList() }; } if (e.key === 'Escape' && opened) { - tempState = Object.assign({}, tempState, Combobox.closeList()); + tempState = { ...tempState, ...Combobox.closeList() }; } if (opened && filteredValues && !filteredValues.length) { - tempState = Object.assign({}, tempState, Combobox.closeList()); + tempState = { ...tempState, ...Combobox.closeList() }; } - this.setState(previousState => Object.assign({}, previousState, tempState)); + this.setState((previousState) => ({ ...previousState, ...tempState })); } getLabelStyles() { @@ -356,7 +411,7 @@ export default class Combobox extends Component { && values.length && !Array.isArray(stateSelectedValues) && !stateSelectedValues.name ) { - return values.filter(value => value.id === stateSelectedValues.id)[0].name; + return values.filter((value) => value.id === stateSelectedValues.id)[0].name; } return stateSelectedValues.name; @@ -390,6 +445,7 @@ export default class Combobox extends Component { return selectedValues; } + // eslint-disable-next-line react/no-unused-class-component-methods manageSelectedValues(index) { const { stateSelectedValues } = this.state; const { @@ -480,7 +536,7 @@ export default class Combobox extends Component { const arr = values .filter( - value => (searchItem + (value) => (searchItem ? searchItem(value, this.inputRef.current.value.toLowerCase()) : Combobox.filterByName(value, this.inputRef.current.value.toLowerCase())), ); @@ -496,99 +552,7 @@ export default class Combobox extends Component { document.removeEventListener('mousedown', this.handleClickOutside); } - handleItemsNavigation(e) { - const { tags } = this.props; - if (['ArrowDown', 'ArrowUp'].indexOf(e.key) === -1) { - return; - } - - const { cursor } = this.state; - const filteredValues = this.filterValues(); - let cursorNext; - - e.preventDefault(); - - if (e.key === 'ArrowDown') { - if (cursor === -1 || cursor === filteredValues.length - 1) { - cursorNext = 0; - } else if (cursor < filteredValues.length - 1) { - cursorNext = cursor + 1; - } - } - - if (e.key === 'ArrowUp') { - if (cursor > 0) { - cursorNext = cursor - 1; - } else { - cursorNext = filteredValues.length - 1; - } - } - - if (this.valuesListRef.current && cursorNext > 4) { - const scrollTop = (cursorNext - 4) * 38; - - this.valuesListRef.current.scrollTop = (!tags) ? 60 + scrollTop : scrollTop; - } else if (this.valuesListRef.current) { - this.valuesListRef.current.scrollTop = 0; - } - - this.setState(previousState => ({ - ...previousState, - opened: true, - cursor: cursorNext, - })); - } - - handleListItemClick(index) { - // eslint-disable-next-line react/destructuring-assignment - const clickedValue = this.props.values[index]; - if (clickedValue.disabled) return; - - const { multiple, tags } = this.props; - - const methodName = (tags) ? 'manageSelectedTags' : 'manageSelectedValues'; - const tempState = Object.assign({}, this.state, this[methodName](index)); - - this.setState((previousState) => { - if (!multiple && !tags) { - tempState.opened = false; - } - - return Object.assign({}, previousState, tempState); - }); - } - - handlePillRemoveClick(index) { - const { disabled } = this.props; - - if (disabled) { - return; - } - - const tempState = this.removeSelectedValue(index); - - this.setState(previousState => Object.assign({}, previousState, tempState)); - } - - handleClickOutside(event) { - if ( - this.valuesListRef.current && !this.valuesListRef.current.contains(event.target) - && this.comboboxRef.current && !this.comboboxRef.current.contains(event.target) - ) { - this.setState(previousState => Object.assign({}, previousState, Combobox.closeList())); - } - } - - handleComboboxDropdownClick() { - const { disabled } = this.props; - - if (disabled) { - return; - } - - this.setState(previousState => Object.assign({}, previousState, Combobox.openList())); - } - + // eslint-disable-next-line react/no-unused-class-component-methods reset() { const { multiple, tags, value } = this.props; @@ -621,7 +585,7 @@ export default class Combobox extends Component { const filteredValues = this.filterValues(); return ( - + <>
{ label @@ -641,11 +605,15 @@ export default class Combobox extends Component { { tags ? ( - + <> { (stateSelectedValues || []).map((value, index) => (
  • - { value.name } @@ -701,7 +669,7 @@ export default class Combobox extends Component { : null }
  • -
    + ) : (
  • ) } - + ); } } + +Combobox.propTypes = { + allowCustomValues: PropTypes.bool, + CustomListComponent: PropTypes.elementType, + searchItem: PropTypes.func, + disabled: PropTypes.bool, + error: PropTypes.string, + id: PropTypes.string, + label: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.node), + PropTypes.node, + PropTypes.element, + PropTypes.func, + PropTypes.string, + ]), + labelwidth: PropTypes.string, + multiple: PropTypes.bool, + max: PropTypes.number, + note: PropTypes.oneOfType([ + PropTypes.node, + PropTypes.element, + PropTypes.string, + ]), + onChange: PropTypes.func, + placeholder: PropTypes.string, + tags: PropTypes.bool, + theme: PropTypes.string, + value: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.instanceOf(Object), + PropTypes.instanceOf(Array), + ]), + values: PropTypes.instanceOf(Array), +}; + +Combobox.defaultProps = { + allowCustomValues: true, +}; + +export default Combobox; diff --git a/src/form/components/Combobox2/Combobox2.test.js b/src/form/components/Combobox2/Combobox2.test.js index 2bf7b89c..6b587120 100644 --- a/src/form/components/Combobox2/Combobox2.test.js +++ b/src/form/components/Combobox2/Combobox2.test.js @@ -55,8 +55,9 @@ describe('Combobox2', () => { .should('have.text', selectedItem.name); cy.get(`.${classes.trigger}`) - .click() - .get(`.${classes.dropdownList} li`) + .click(); + + cy.get(`.${classes.dropdownList} li`) .should('have.length', items.length) .filter(`[title="${selectedItem.name}"]`) .find(`.${classes.checkIcon}`); @@ -72,8 +73,9 @@ describe('Combobox2', () => { cy.get(`.${classes.dropdownList} li`) .eq(1) - .click() - .get(`.${classes.textValue}`) + .click(); + + cy.get(`.${classes.textValue}`) .should('have.class', 'is-placeholder') .and('have.text', placeholder); }); @@ -102,11 +104,14 @@ describe('Combobox2', () => { } cy.mount(); - const changeValueAndCheck = (id) => cy - .contains('change value') - .click() - .get(`.${classes.textValue}`) - .should('have.text', getItemNameById(id)); + const changeValueAndCheck = (id) => { + cy + .contains('change value') + .click(); + + return cy.get(`.${classes.textValue}`) + .should('have.text', getItemNameById(id)); + }; cy.get(`.${classes.textValue}`) .should('have.text', getItemNameById(values[0])) @@ -142,8 +147,9 @@ describe('Combobox2', () => { .should('be.gt', singleHeight) .get('#single-checkbox') - .check() - .get(`.${classes.textValue}`) + .check(); + + cy.get(`.${classes.textValue}`) .invoke('css', 'height') .then(parseFloat) .should('eq', singleHeight); @@ -162,14 +168,17 @@ describe('Combobox2', () => { ); cy.get(`.${classes.entryField}`) - .type('aaa') - .blur() - .get('.tyk-pill:last-of-type') + .type('aaa'); + cy.get(`.${classes.entryField}`) + .blur(); + + cy.get('.tyk-pill:last-of-type') .should('have.text', 'aaa') .get(`.${classes.entryField}`) - .type('bbb.') - .get('.tyk-pill') + .type('bbb.'); + + cy.get('.tyk-pill') .should('have.length', 2) .filter(':last-of-type') .should('have.text', 'bbb'); @@ -186,8 +195,9 @@ describe('Combobox2', () => { ); cy.get(`.${classes.trigger}`) - .click() - .get(`.${classes.dropdownList}`) + .click(); + + cy.get(`.${classes.dropdownList}`) .should('not.exist'); }); @@ -221,13 +231,15 @@ describe('Combobox2', () => { .should('have.length', 1) .get(`.${classes.entryField}`) - .type('nonexistent{enter}') - .get(`.${classes.tag}`) + .type('nonexistent{enter}'); + + cy.get(`.${classes.tag}`) .should('have.length', 1) .get(`.${classes.entryField}`) - .type('Item2{enter}') - .get(`.${classes.tag}`) + .type('Item2{enter}'); + + cy.get(`.${classes.tag}`) .should('have.length', 2); }); @@ -245,13 +257,15 @@ describe('Combobox2', () => { ); cy.get(`.${classes.trigger}`) - .click() - .get(`.${classes.dropdownList} li`) + .click(); + + cy.get(`.${classes.dropdownList} li`) .should('have.length', items.length) .get(`.${classes.searchField} input`) - .type('2') - .get(`.${classes.dropdownList} li`) + .type('2'); + + cy.get(`.${classes.dropdownList} li`) .should('have.length', 1) .and('have.text', 'Item4'); }); @@ -273,8 +287,9 @@ describe('Combobox2', () => { .should('have.length', 2); cy.get(`.${classes.trigger}`) - .click() - .get(`.${classes.dropdownList} li`) + .click(); + + cy.get(`.${classes.dropdownList} li`) .should('not.exist') .get(`.${classes.dropdownList} .custom-item`) .should('have.length', items.length); @@ -297,8 +312,9 @@ describe('Combobox2', () => { ); cy.get(`.${classes.trigger}`) - .click() - .get(`.${classes.dropdown} .${classes.dropdownList}`) + .click(); + + cy.get(`.${classes.dropdown} .${classes.dropdownList}`) .should('not.exist') .get(`.${classes.dropdown} .custom-list`) .should('exist') @@ -328,8 +344,9 @@ describe('Combobox2', () => { .should('eq', normalHeight) .get(`.${classes.trigger}`) - .click() - .get(`.${classes.valueContainer}`) + .click(); + + cy.get(`.${classes.valueContainer}`) .invoke('css', 'height') .then(parseFloat) .should('be.gt', normalHeight); @@ -363,8 +380,9 @@ describe('Combobox2', () => { .should('exist') .get('#display-trigger') - .uncheck() - .get(`.${classes.trigger}`) + .uncheck(); + + cy.get(`.${classes.trigger}`) .should('not.exist'); }); @@ -380,31 +398,36 @@ describe('Combobox2', () => { cy.get(`.${classes.tag}`) .should('have.length', 0) .get(`.${classes.entryField}`) - .focus() - .get('li.active') + .focus(); + + cy.get('li.active') .should('not.exist') .get(`.${classes.entryField}`) - .type('{downarrow}') - .get('li:first') + .type('{downarrow}'); + + cy.get('li:first') .should('have.class', 'active') .get(`.${classes.entryField}`) - .type('{uparrow}') - .get('li:first') + .type('{uparrow}'); + + cy.get('li:first') .should('not.have.class', 'active') .get('li:last') .should('have.class', 'active') .get(`.${classes.entryField}`) - .type('{enter}') - .get(`.${classes.tag}`) + .type('{enter}'); + + cy.get(`.${classes.tag}`) .should('have.length', 1) .and('have.text', items[items.length - 1].name) .get(`.${classes.entryField}`) - .type('{esc}') - .get('.tyk-combobox2__combobox-dropdown') + .type('{esc}'); + + cy.get('.tyk-combobox2__combobox-dropdown') .should('not.exist'); }); @@ -430,9 +453,9 @@ describe('Combobox2', () => { .should('have.length', 0) .get(`.${classes.entryField}`) - .focus() + .focus(); - .then(getSelectAll) + getSelectAll() .click() .find(`.${classes.checkIcon}`) .should('exist') @@ -472,11 +495,13 @@ describe('Combobox2', () => { ); cy.get(`.${classes.trigger}`) - .click() - .get(`.${classes.dropdownList} li`) + .click(); + + cy.get(`.${classes.dropdownList} li`) .eq(1) - .click() - .get(`.${classes.textValue}`) + .click(); + + cy.get(`.${classes.textValue}`) .should('have.text', previousItem.name); }); @@ -494,11 +519,13 @@ describe('Combobox2', () => { />, ); - // eslint-disable-next-line cypress/no-unnecessary-waiting cy.get(`.${classes.entryField}`) - .type('aaa') - .blur() - .wait(1000) + .type('aaa'); + cy.get(`.${classes.entryField}`) + .blur(); + + // eslint-disable-next-line cypress/no-unnecessary-waiting + cy.wait(1000) .get('.tyk-pill:last-of-type') .should('have.text', item.name); }); @@ -518,9 +545,11 @@ describe('Combobox2', () => { ); cy.get(`.${classes.entryField}`) - .type('{backspace}') - .blur() - .get('.tyk-pill') + .type('{backspace}'); + cy.get(`.${classes.entryField}`) + .blur(); + + cy.get('.tyk-pill') .should('have.length', 1) .filter(':last-of-type') .should('have.text', item.name); diff --git a/src/form/components/Combobox2/index.js b/src/form/components/Combobox2/index.js index 2260d241..f3953f76 100644 --- a/src/form/components/Combobox2/index.js +++ b/src/form/components/Combobox2/index.js @@ -1,4 +1,6 @@ -import React, { useState, useRef, useEffect } from 'react'; +import React, { + useCallback, useEffect, useRef, useState, +} from 'react'; import PropTypes from 'prop-types'; import Icon from '../../../components/Icon'; @@ -325,7 +327,7 @@ function Combobox2({ } } - function onMessage(message, data) { + const onMessage = useCallback((message, data) => { if (message.startsWith('tag.')) onTagMessage(message.slice(4), data); if (message === 'value.select') selectValue(data.item); @@ -345,7 +347,16 @@ function Combobox2({ if (message === 'search.enter') { if (activeItem) selectValue(activeItem); } - } + }, [ + onTagMessage, + selectValue, + selectAllValues, + openDropdown, + updateSearchValue, + closeDropdown, + moveUpActiveItem, + moveDownActiveItem, + ]); useEffect(() => { window.addEventListener('click', handleDocumentClick, true); @@ -444,7 +455,7 @@ function Combobox2({ role="button" tabIndex={disabled ? -1 : 0} onClick={executeTriggerAction} - onKeyPress={executeTriggerAction} + onKeyDown={executeTriggerAction} > {expandMode ? ( diff --git a/src/form/components/Combobox2/js/List.js b/src/form/components/Combobox2/js/List.js index d519fa24..1d2a298c 100644 --- a/src/form/components/Combobox2/js/List.js +++ b/src/form/components/Combobox2/js/List.js @@ -37,7 +37,7 @@ function List(props) { activeItem && item.id === activeItem.id && 'active', ].filter(Boolean).join(' ')} onClick={() => !item.disabled && sendMessage('value.select', { item })} - onKeyPress={() => {}} + onKeyDown={() => {}} > {item.selected && } {item.name} @@ -46,8 +46,8 @@ function List(props) { } function renderSelectAllOption() { - const isAllSelected = values.every(v => v.selected); - const isNoneSelected = values.every(v => !v.selected); + const isAllSelected = values.every((v) => v.selected); + const isNoneSelected = values.every((v) => !v.selected); const label = selectAll?.label ?? 'Select All'; const mode = selectAll?.mode ?? 'select'; const show = selectAll?.show ?? 'always'; @@ -84,7 +84,7 @@ function List(props) { autoFocus className="tyk-form-control" value={searchValue} - onChange={e => sendMessage('search.change', e.target.value)} + onChange={(e) => sendMessage('search.change', e.target.value)} onKeyDown={(e) => { if (e.key === 'Escape') sendMessage('search.escape'); if (e.key === 'ArrowUp') sendMessage('search.arrowUp'); diff --git a/src/form/components/Combobox2/js/Value.js b/src/form/components/Combobox2/js/Value.js index 940dd71d..b5847e1d 100644 --- a/src/form/components/Combobox2/js/Value.js +++ b/src/form/components/Combobox2/js/Value.js @@ -1,4 +1,5 @@ import React, { useState, useRef, useEffect } from 'react'; +import PropTypes from 'prop-types'; import Icon from '../../../../components/Icon'; import Pill from '../../../../components/Pill'; @@ -24,7 +25,7 @@ function Value(props) { const [inputWidth, setInputWidth] = useState(`${INPUT_MIN_WIDTH + 10}px`); function entryFieldOnChange(val) { - if (tagSeparators.some(ts => val.slice(-1) === ts)) return; + if (tagSeparators.some((ts) => val.slice(-1) === ts)) return; const dummy = dummyElementRef.current; dummy.innerHTML = val; setInputWidth(`${Math.max(dummy.clientWidth, INPUT_MIN_WIDTH) + 10}px`); @@ -113,6 +114,7 @@ function Value(props) { e.stopPropagation(); sendMessage('tag.remove', v.id); }} + aria-label="remove" > @@ -134,7 +136,7 @@ function Value(props) { disabled={disabled} value={inputValue} style={{ width: value.length ? inputWidth : '100%' }} - onChange={e => entryFieldOnChange(e.target.value)} + onChange={(e) => entryFieldOnChange(e.target.value)} onKeyDown={(e) => { if (e.key === 'Enter') e.preventDefault(); entryFieldOnKeyDown(e.key, e.target.value); @@ -164,7 +166,7 @@ function Value(props) { }, [focus]); if (readOnly) { - return
    {textValue(value.map(v => v.name).join(', '))}
    ; + return
    {textValue(value.map((v) => v.name).join(', '))}
    ; } if (tags) return getTags(); @@ -173,7 +175,22 @@ function Value(props) { } return value.length === 0 ? textValue(placeholder, true) - : textValue(value.map(v => v.name).join(', ')); + : textValue(value.map((v) => v.name).join(', ')); } +Value.propTypes = { + value: PropTypes.instanceOf(Array), + max: PropTypes.number, + tags: PropTypes.instanceOf(Array), + tagSeparators: PropTypes.instanceOf(Array), + addTagOnBlur: PropTypes.bool, + placeholder: PropTypes.string, + disabled: PropTypes.bool, + renderValue: PropTypes.func, + valueOverflow: PropTypes.string, + focus: PropTypes.bool, + onMessage: PropTypes.func, + readOnly: PropTypes.bool, +}; + export default Value; diff --git a/src/form/components/DatePicker/DatePicker.test.js b/src/form/components/DatePicker/DatePicker.test.js index 53b6d2d7..e2226671 100644 --- a/src/form/components/DatePicker/DatePicker.test.js +++ b/src/form/components/DatePicker/DatePicker.test.js @@ -39,16 +39,18 @@ describe('DatePicker', () => { cy.mount() .get(selectors.input) .should('exist') - .focus() - .get(selectors.calendar) + .focus(); + + cy.get(selectors.calendar) .should('be.visible'); }); it('can open the calendar by clicking on the calendar icon', () => { cy.mount() .get(selectors.icon) - .click() - .get(selectors.calendar) + .click(); + + cy.get(selectors.calendar) .should('be.visible'); }); @@ -66,8 +68,9 @@ describe('DatePicker', () => { , ) .get('#datepicker-button') - .click() - .get(selectors.calendar) + .click(); + + cy.get(selectors.calendar) .should('be.visible'); }); @@ -137,8 +140,9 @@ describe('DatePicker', () => { const onOpen = cy.stub().as('onOpen'); cy.mount() .get(selectors.input) - .focus() - .get('@onOpen') + .focus(); + + cy.get('@onOpen') .should('be.called'); }); @@ -151,10 +155,12 @@ describe('DatePicker', () => { , ) .get(selectors.input) - .focus() - .get('#close-button') - .click() - .get('@onClose') + .focus(); + + cy.get('#close-button') + .click(); + + cy.get('@onClose') .should('be.called'); }); @@ -162,11 +168,13 @@ describe('DatePicker', () => { const onChange = cy.stub().as('onChange'); cy.mount() .get(selectors.input) - .focus() - .get(selectors.day) + .focus(); + + cy.get(selectors.day) .eq(10) - .click() - .get('@onChange') + .click(); + + cy.get('@onChange') .should('be.called'); }); @@ -194,14 +202,17 @@ describe('DatePicker', () => { const secondDay = 12; cy.mount( {}} />) .get(selectors.icon) - .click() - .get(selectors.day) + .click(); + + cy.get(selectors.day) .contains(firstDay) - .click() - .get(selectors.day) + .click(); + + cy.get(selectors.day) .contains(secondDay) - .click() - .get(selectors.input) + .click(); + + cy.get(selectors.input) .should('have.value', `${year}-${month}-${firstDay} to ${year}-${month}-${secondDay}`); }); diff --git a/src/form/components/DatePicker/index.js b/src/form/components/DatePicker/index.js index 42111c78..fd06ed62 100644 --- a/src/form/components/DatePicker/index.js +++ b/src/form/components/DatePicker/index.js @@ -165,6 +165,7 @@ function DatePicker({ onKeyUp={() => {}} tabIndex={0} style={addonStyle} + aria-label="show calendar" >
  • diff --git a/src/form/components/Dropdown/Dropdown.test.js b/src/form/components/Dropdown/Dropdown.test.js index 3d756f08..615ec7b4 100644 --- a/src/form/components/Dropdown/Dropdown.test.js +++ b/src/form/components/Dropdown/Dropdown.test.js @@ -41,8 +41,9 @@ describe('Dropdown', () => { .get(selectors.menu) .should('not.exist') .get(selectors.trigger) - .click() - .get(selectors.menu) + .click(); + + cy.get(selectors.menu) .should('exist'); }); @@ -63,8 +64,9 @@ describe('Dropdown', () => { ); cy.get(selectors.trigger) - .click() - .get('#target-container') + .click(); + + cy.get('#target-container') .find(selectors.menu) .should('exist'); }); @@ -73,8 +75,9 @@ describe('Dropdown', () => { cy.mount() .get(selectors.item) .eq(0) - .click() - .get(selectors.menu) + .click(); + + cy.get(selectors.menu) .should('not.exist'); }); @@ -131,8 +134,9 @@ describe('Dropdown', () => { it('the trigger button can be disabled', () => { cy.mount() .get(selectors.trigger) - .click({ force: true }) - .get(selectors.menu) + .click({ force: true }); + + cy.get(selectors.menu) .should('not.exist'); }); @@ -153,10 +157,12 @@ describe('Dropdown', () => { const onClose = cy.stub().as('onClose'); cy.mount() .get(selectors.trigger) - .click() - .get('body') - .click() - .get('@onClose') + .click(); + + cy.get('body') + .click(); + + cy.get('@onClose') .should('be.called'); }); @@ -164,11 +170,13 @@ describe('Dropdown', () => { const onSelect = cy.stub().as('onSelect'); cy.mount() .get(selectors.trigger) - .click() - .get(selectors.item) + .click(); + + cy.get(selectors.item) .eq(0) - .click() - .get('@onSelect') + .click(); + + cy.get('@onSelect') .should('be.called'); }); @@ -214,8 +222,9 @@ describe('Dropdown', () => { .should('have.text', label) .get(selectors.item) .eq(0) - .click() - .get(selectors.trigger) + .click(); + + cy.get(selectors.trigger) .should('have.text', label); }); @@ -235,8 +244,9 @@ describe('Dropdown', () => { ) .get(selectors.item) .eq(0) - .click() - .get('@onClick') + .click(); + + cy.get('@onClick') .should('be.calledWith', itemId); }); diff --git a/src/form/components/Dropdown/index.js b/src/form/components/Dropdown/index.js index 5cd13009..cf8990a3 100644 --- a/src/form/components/Dropdown/index.js +++ b/src/form/components/Dropdown/index.js @@ -327,6 +327,7 @@ class Dropdown extends Component { opened ? ReactDOM.createPortal( ({ ...previousState, ...tempState }), () => { + this.triggerOnChange(tempState.value.length - 1, null, value); + }); + } + + handleItemUpdate(index, value) { + let prevValue; + this.setState((previousState) => { + const tempState = { ...previousState }; + prevValue = fromJS(tempState.value[index].value).toJS(); + tempState.value[index].value = value; + tempState.value[index].editMode = false; + + return tempState; + }, () => { + this.triggerOnChange(index, prevValue, value); + }); + } + + handleChildrenOnChange(index, value) { + let prevValue; + this.setState((previousState) => { + const tempState = { ...previousState }; + prevValue = fromJS(tempState.value[index].children).toJS(); + tempState.value[index].children = value; + + return tempState; + }, () => { + this.triggerOnChange(index, prevValue, value); + }); + } + getListItemsCssClass(displayType) { const { config } = this.props; const cssClasses = ['tyk-editable-list-items']; @@ -117,40 +132,40 @@ export default class EditableList extends Component { let value = null; switch (config.displayType) { - case 'list': - case 'inline': { - const itemText = itemData.value.reduce((prevValue, itemValue, index) => { - const tempValue = EditableList.getItemListValue(itemValue); - let separator = ', '; - - if (index === itemData.value.length - 1) { - separator = ''; - } + case 'list': + case 'inline': { + const itemText = itemData.value.reduce((prevValue, itemValue, index) => { + const tempValue = EditableList.getItemListValue(itemValue); + let separator = ', '; + + if (index === itemData.value.length - 1) { + separator = ''; + } - return prevValue + tempValue + separator; - }, ''); + return prevValue + tempValue + separator; + }, ''); - value = itemText; + value = itemText; - break; - } - default: - value = ( - - { - config.components.map((component, index) => ( - - { EditableList.getItemListValue(itemData.value[index]) } - - )) - } - - ); - - break; + break; + } + default: + value = ( + + { + config.components.map((component, index) => ( + + { EditableList.getItemListValue(itemData.value[index]) } + + )) + } + + ); + + break; } return value; @@ -166,7 +181,7 @@ export default class EditableList extends Component { const newValues = this.closeListItems(); this.setState((previousState) => { - const tempState = Object.assign({}, previousState); + const tempState = { ...previousState }; tempState.value = newValues; tempState.value[index].editMode = !tempState.value[index].editMode; @@ -225,45 +240,11 @@ export default class EditableList extends Component { }); } - handleFormSubmit(value) { - const tempState = this.addValues(value); - this.setState(previousState => Object.assign({}, previousState, tempState), () => { - this.triggerOnChange(tempState.value.length - 1, null, value); - }); - } - - handleItemUpdate(index, value) { - let prevValue; - this.setState((previousState) => { - const tempState = Object.assign({}, previousState); - prevValue = fromJS(tempState.value[index].value).toJS(); - tempState.value[index].value = value; - tempState.value[index].editMode = false; - - return tempState; - }, () => { - this.triggerOnChange(index, prevValue, value); - }); - } - - handleChildrenOnChange(index, value) { - let prevValue; - this.setState((previousState) => { - const tempState = Object.assign({}, previousState); - prevValue = fromJS(tempState.value[index].children).toJS(); - tempState.value[index].children = value; - - return tempState; - }, () => { - this.triggerOnChange(index, prevValue, value); - }); - } - closeListItems() { const { value } = this.state; const newValues = value.map((itemValue) => { - const tempItemValue = Object.assign({}, itemValue); + const tempItemValue = { ...itemValue }; if (tempItemValue.editMode) { tempItemValue.editMode = false; @@ -307,7 +288,7 @@ export default class EditableList extends Component { { !itemData.editMode ? ( - + <> { this.getListItemText(itemData, index) }
    - -
    -
    + ) : ( - + <> - - + ) } @@ -372,7 +370,7 @@ export default class EditableList extends Component { } return ( - + <> { config.displayType === 'table' || !config.displayType ? ( @@ -400,7 +398,7 @@ export default class EditableList extends Component { value.map((itemData, index) => this.genListItem(itemData, index)) } - + ); } @@ -444,3 +442,24 @@ export default class EditableList extends Component { ); } } + +EditableList.propTypes = { + addValueOnFieldChange: PropTypes.bool, + disabled: PropTypes.bool, + error: PropTypes.string, + config: PropTypes.instanceOf(Object), + onChange: PropTypes.func, + label: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.node), + PropTypes.node, + PropTypes.element, + PropTypes.func, + PropTypes.string, + ]), + value: PropTypes.oneOfType([ + PropTypes.instanceOf(Array), + PropTypes.instanceOf(Object), + ]), +}; + +export default EditableList; diff --git a/src/form/components/EditableList/js/EditableListForm.js b/src/form/components/EditableList/js/EditableListForm.js index ff7924f0..930bcd50 100644 --- a/src/form/components/EditableList/js/EditableListForm.js +++ b/src/form/components/EditableList/js/EditableListForm.js @@ -6,23 +6,7 @@ import Column from '../../../../layout/Column'; import Row from '../../../../layout/Row'; import Button from '../../../../components/Button'; -export default class EditableListForm extends Component { - static propTypes = { - addValueOnFieldChange: PropTypes.bool, - noLabels: PropTypes.bool, - buttonName: PropTypes.string, - buttonStyle: PropTypes.string, - components: PropTypes.instanceOf(Array), - displayType: PropTypes.string, - disabled: PropTypes.bool, - getMainFormButtonWidth: PropTypes.func, - error: PropTypes.string, - errorPersist: PropTypes.bool, - onSubmit: PropTypes.func, - validate: PropTypes.func, - validationmessage: PropTypes.string, - }; - +class EditableListForm extends Component { static getMainFormValue(components) { const mainFormValue = new Array(components.length); @@ -91,6 +75,27 @@ export default class EditableListForm extends Component { return state; } + handleOnChange(component, index, value) { + const { addValueOnFieldChange } = this.props; + let tempState = this.state; + + tempState = { ...tempState, ...this.validateValue(value, component.props) }; + + if (!tempState.errors[component.props.name]) { + tempState.mainFormValue[index] = value; + + if (component.props.onChange) { + component.props.onChange(value); + } + } + + this.setState((previousState) => ({ ...previousState, ...tempState }), () => { + if (addValueOnFieldChange) { + this.submitForm(); + } + }); + } + getFormCssClasses() { const { noLabels } = this.props; const cssClasses = ['tyk-editable-list__form']; @@ -153,7 +158,7 @@ export default class EditableListForm extends Component { const validatorsNames = props.validate ? Object.keys(props.validate) : []; // if there is a general form error don't take field errors into consideration const tempState = { - errors: (error || mainError) ? {} : Object.assign({}, errors), + errors: (error || mainError) ? {} : { ...errors }, }; let ok = true; @@ -186,7 +191,7 @@ export default class EditableListForm extends Component { const { components, errorPersist } = this.props; const { mainFormValue } = this.state; const tempState = { - errors: Object.assign({}, errors), + errors: { ...errors }, }; if (errorPersist && mainFormValue.indexOf(undefined) === -1) { @@ -198,37 +203,15 @@ export default class EditableListForm extends Component { } components.forEach((component, index) => { - tempState.errors = Object.assign( - {}, - tempState.errors, - this.validateValue(mainFormValue[index], component.props).errors, - ); + tempState.errors = { + ...tempState.errors, + ...this.validateValue(mainFormValue[index], component.props).errors, + }; }); return Object.keys(tempState.errors).length > 0; } - handleOnChange(component, index, value) { - const { addValueOnFieldChange } = this.props; - let tempState = this.state; - - tempState = Object.assign({}, tempState, this.validateValue(value, component.props)); - - if (!tempState.errors[component.props.name]) { - tempState.mainFormValue[index] = value; - - if (component.props.onChange) { - component.props.onChange(value); - } - } - - this.setState(previousState => Object.assign({}, previousState, tempState), () => { - if (addValueOnFieldChange) { - this.submitForm(); - } - }); - } - resetForm() { const { refs } = this.state; const { @@ -356,3 +339,21 @@ export default class EditableListForm extends Component { ); } } + +EditableListForm.propTypes = { + addValueOnFieldChange: PropTypes.bool, + noLabels: PropTypes.bool, + buttonName: PropTypes.string, + buttonStyle: PropTypes.string, + components: PropTypes.instanceOf(Array), + displayType: PropTypes.string, + disabled: PropTypes.bool, + getMainFormButtonWidth: PropTypes.func, + error: PropTypes.string, + errorPersist: PropTypes.bool, + onSubmit: PropTypes.func, + validate: PropTypes.func, + validationmessage: PropTypes.string, +}; + +export default EditableListForm; diff --git a/src/form/components/EditableList2/Header.js b/src/form/components/EditableList2/Header.js index 4deb5f59..05d1a636 100644 --- a/src/form/components/EditableList2/Header.js +++ b/src/form/components/EditableList2/Header.js @@ -3,25 +3,27 @@ import PropTypes from 'prop-types'; import Button from '../../../components/Button'; -const Header = ({ +function Header({ label, addButtonName, onAddRow, disabled, readOnly, -}) => ( -
    - { - label - ? - : null - } - {!disabled && !readOnly && ( - - )} -
    -); +}) { + return ( +
    + { + label + ? + : null + } + {!disabled && !readOnly && ( + + )} +
    + ); +} Header.propTypes = { disabled: PropTypes.bool, diff --git a/src/form/components/EditableList2/with-validation.js b/src/form/components/EditableList2/with-validation.js index 0e0ea50f..432420fb 100644 --- a/src/form/components/EditableList2/with-validation.js +++ b/src/form/components/EditableList2/with-validation.js @@ -1,9 +1,10 @@ import React, { useCallback, useState } from 'react'; -const withValidation = Component => ({ - onChange, - validate, - error, +// eslint-disable-next-line react/function-component-definition +const withValidation = (Component) => ({ + onChange, // eslint-disable-line react/prop-types + validate, // eslint-disable-line react/prop-types + error, // eslint-disable-line react/prop-types ...rest }) => { const [internalError, setInternalError] = useState(null); diff --git a/src/form/components/Input/index.js b/src/form/components/Input/index.js index 6efb14e6..164ac803 100644 --- a/src/form/components/Input/index.js +++ b/src/form/components/Input/index.js @@ -1,43 +1,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -export default class Input extends Component { - static propTypes = { - disabled: PropTypes.bool, - readOnly: PropTypes.bool, - id: PropTypes.string, - isfield: PropTypes.bool, - error: PropTypes.oneOfType([ - PropTypes.string, - PropTypes.bool, - ]), - inputgroupaddonleft: PropTypes.oneOfType([ - PropTypes.element, - PropTypes.node, - PropTypes.string, - ]), - inputgroupaddonright: PropTypes.oneOfType([ - PropTypes.element, - PropTypes.node, - PropTypes.string, - ]), - label: PropTypes.oneOfType([ - PropTypes.string, - PropTypes.element, - PropTypes.node, - ]), - labelwidth: PropTypes.string, - name: PropTypes.string, - note: PropTypes.string, - onChange: PropTypes.func, - placeholder: PropTypes.string, - theme: PropTypes.string, - value: PropTypes.oneOfType([ - PropTypes.number, - PropTypes.string, - ]), - } - +class Input extends Component { static getAddon(content) { return (
    @@ -79,6 +43,23 @@ export default class Input extends Component { } } + handleOnChange(e) { + const { onChange, isfield } = this.props; + const inputValue = e.target.value; + + if (!isfield) { + this.setState({ + stateValue: inputValue, + }, () => { + if (onChange) { + onChange(inputValue); + } + }); + } else { + onChange(inputValue); + } + } + getLabelStyles() { const { labelwidth } = this.props; const styles = {}; @@ -190,6 +171,7 @@ export default class Input extends Component { ); } + // eslint-disable-next-line react/no-unused-class-component-methods reset() { const { initValue } = this.state; @@ -198,23 +180,6 @@ export default class Input extends Component { }); } - handleOnChange(e) { - const { onChange, isfield } = this.props; - const inputValue = e.target.value; - - if (!isfield) { - this.setState({ - stateValue: inputValue, - }, () => { - if (onChange) { - onChange(inputValue); - } - }); - } else { - onChange(inputValue); - } - } - render() { const { label, @@ -258,3 +223,41 @@ export default class Input extends Component { ); } } + +Input.propTypes = { + disabled: PropTypes.bool, + readOnly: PropTypes.bool, + id: PropTypes.string, + isfield: PropTypes.bool, + error: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.bool, + ]), + inputgroupaddonleft: PropTypes.oneOfType([ + PropTypes.element, + PropTypes.node, + PropTypes.string, + ]), + inputgroupaddonright: PropTypes.oneOfType([ + PropTypes.element, + PropTypes.node, + PropTypes.string, + ]), + label: PropTypes.oneOfType([ + PropTypes.string, + PropTypes.element, + PropTypes.node, + ]), + labelwidth: PropTypes.string, + name: PropTypes.string, + note: PropTypes.string, + onChange: PropTypes.func, + placeholder: PropTypes.string, + theme: PropTypes.string, + value: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.string, + ]), +}; + +export default Input; diff --git a/src/form/components/Input2/index.js b/src/form/components/Input2/index.js index 01f254d8..305586a3 100644 --- a/src/form/components/Input2/index.js +++ b/src/form/components/Input2/index.js @@ -24,7 +24,7 @@ const Input2 = forwardRef(({ }, })); - const getAddon = content => ( + const getAddon = (content) => (
    { content }
    diff --git a/src/form/components/StringBuilder/StringBuilder.test.js b/src/form/components/StringBuilder/StringBuilder.test.js index 664a5c0a..0a3942f0 100644 --- a/src/form/components/StringBuilder/StringBuilder.test.js +++ b/src/form/components/StringBuilder/StringBuilder.test.js @@ -3,7 +3,7 @@ import StringBuilder from './index'; describe('StringBuilder', () => { it('renders the component with proper classes and attributes based on props and tests basic functionality', () => { - const onChangeMock = cy.stub(); + const onChangeMock = cy.stub().as('onChangeMock'); const initialVal = 'https://petstore.swagger.io/v2/pet/{{.arguments.id}}/XdXd/{{.arguments.id}}'; const options = [ { @@ -28,23 +28,32 @@ describe('StringBuilder', () => { // argument selection from dropdown cy.get('.string-builder__input') .should('have.value', initialVal) - .type('{') - .get('.string-builder-list li') - .click() - .then(() => expect(onChangeMock).to.be.called); + .type('{'); + + cy.get('.string-builder-list li') + .click(); + + cy.get('@onChangeMock') + .should('be.called'); // Backspace on argument should delete the whole argument instead of single character cy.get('.string-builder__input') .should('have.value', `${initialVal}${options[0].id}`) - .type('{backspace}') + .type('{backspace}'); + + cy.get('.string-builder__input') .should('have.value', initialVal); // Backspace on normal character should delete only single character cy.get('.string-builder__input') .should('have.value', initialVal) - .type('foo') + .type('foo'); + + cy.get('.string-builder__input') .should('have.value', `${initialVal}foo`) - .type('{backspace}') + .type('{backspace}'); + + cy.get('.string-builder__input') .should('have.value', `${initialVal}fo`); }); }); diff --git a/src/form/components/StringBuilder/js/invalid-token.js b/src/form/components/StringBuilder/js/invalid-token.js index 8eb17a69..f671d887 100644 --- a/src/form/components/StringBuilder/js/invalid-token.js +++ b/src/form/components/StringBuilder/js/invalid-token.js @@ -3,8 +3,7 @@ import PropTypes from 'prop-types'; import Tooltip from '../../../../components/Tooltip'; import Icon from '../../../../components/Icon'; - -const InvalidToken = (props) => { +function InvalidToken(props) { const { token, findInvalidTokenSubstitute } = props; return ( { ); -}; +} InvalidToken.propTypes = { token: PropTypes.string, diff --git a/src/form/components/StringBuilder/js/options-list.js b/src/form/components/StringBuilder/js/options-list.js index 4b4bafac..681ee800 100644 --- a/src/form/components/StringBuilder/js/options-list.js +++ b/src/form/components/StringBuilder/js/options-list.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import FloatingContainer from '../../../../components/FloatingContainer'; -export const OptionsList = ({ +export function OptionsList({ options, showOptions, handleOptionSelection, @@ -12,7 +12,7 @@ export const OptionsList = ({ setShowOptions, allowSearch, initialSearchValue, -}) => { +}) { const [filterValue, setFilterValue] = useState(initialSearchValue); const [filteredOptions, setFilteredOptions] = useState(options); @@ -26,7 +26,7 @@ export const OptionsList = ({ return; } const newFilteredOptions = options.filter( - option => option.name.toLowerCase().includes(filterValue.toLowerCase()), + (option) => option.name.toLowerCase().includes(filterValue.toLowerCase()), ); setFilteredOptions(newFilteredOptions); }, [filterValue]); @@ -40,58 +40,56 @@ export const OptionsList = ({ } return ( - <> - - {allowSearch && ( -
    - { - setFilterValue(e.target.value); - }} - onKeyDown={(e) => { - if ( - e.key === 'Escape' - || e.key === 'ArrowUp' - // || e.key === 'ArrowDown' - || e.key === 'Enter' - ) { - setShowOptions(false); - } - }} - /> -
    - )} -
      - {filteredOptions.map(option => ( -
    • handleOptionSelection(option)} - > - - {option.name} + + {allowSearch && ( +
      + { + setFilterValue(e.target.value); + }} + onKeyDown={(e) => { + if ( + e.key === 'Escape' + || e.key === 'ArrowUp' + // || e.key === 'ArrowDown' + || e.key === 'Enter' + ) { + setShowOptions(false); + } + }} + /> +
      + )} +
        + {filteredOptions.map((option) => ( +
      • handleOptionSelection(option)} + > + + {option.name} + + {option.desc && ( + +   :   + {option.desc} - {option.desc && ( - -   :   - {option.desc} - - )} -
      • - ))} -
      -
      - + )} +
    • + ))} +
    +
    ); -}; +} OptionsList.propTypes = { options: PropTypes.arrayOf(Object), diff --git a/src/form/components/StringBuilder/js/service.js b/src/form/components/StringBuilder/js/service.js index 0fcce2db..12abad3d 100644 --- a/src/form/components/StringBuilder/js/service.js +++ b/src/form/components/StringBuilder/js/service.js @@ -6,7 +6,6 @@ export const stringToTokenString = (value, options) => { return tempStr; }; - export const setCursorPos = (ref, pos) => { setTimeout(() => { ref.current.setSelectionRange(pos, pos); diff --git a/src/form/components/StringBuilder/js/string-builder-footer.js b/src/form/components/StringBuilder/js/string-builder-footer.js index 25f8c734..b183b44b 100644 --- a/src/form/components/StringBuilder/js/string-builder-footer.js +++ b/src/form/components/StringBuilder/js/string-builder-footer.js @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; -export const StringBuilderFooter = (props) => { +export function StringBuilderFooter(props) { const { note, error, inputInFocus, stringBuilderHeight, dropdownTriggerKey, } = props; @@ -29,7 +29,7 @@ export const StringBuilderFooter = (props) => { )}
    ); -}; +} StringBuilderFooter.propTypes = { note: PropTypes.string, diff --git a/src/form/components/StringBuilder/js/string-input.js b/src/form/components/StringBuilder/js/string-input.js index f4d346bc..d14821d2 100644 --- a/src/form/components/StringBuilder/js/string-input.js +++ b/src/form/components/StringBuilder/js/string-input.js @@ -82,8 +82,8 @@ function StringInput({ // -- START :: Handle backspacing when cursor is at the end of the string if (selectionEnd === tokenValue.length) { - const lastToken = tokens[tokens?.length - 2]; - const lastCharsInString = tokenValue.slice(-lastToken?.length); + const lastToken = tokens[tokens.length - 2]; + const lastCharsInString = tokenValue.slice(-(lastToken?.length ?? 0)); if (lastToken === lastCharsInString) { e.preventDefault(); setTokenString( diff --git a/src/form/components/StringBuilder/js/tokenized-string.js b/src/form/components/StringBuilder/js/tokenized-string.js index 70c9d643..62dfa152 100644 --- a/src/form/components/StringBuilder/js/tokenized-string.js +++ b/src/form/components/StringBuilder/js/tokenized-string.js @@ -2,7 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import InvalidToken from './invalid-token'; -export const TokenizedString = (props) => { +export function TokenizedString(props) { const { tokens, options, @@ -23,7 +23,7 @@ export const TokenizedString = (props) => { const allTokens = tokens && tokens.map((token) => { if (options) { - const matchedOption = options.find(option => option.id === token); + const matchedOption = options.find((option) => option.id === token); // if invalid token if (invalidTokenRegex && !matchedOption) { const matchedTokens = token.replaceAll(' ', '').split(invalidTokenRegex); @@ -71,7 +71,7 @@ export const TokenizedString = (props) => { {allTokens} ); -}; +} TokenizedString.propTypes = { tokens: PropTypes.arrayOf(PropTypes.string), diff --git a/src/form/components/Toggle/Toggle.test.js b/src/form/components/Toggle/Toggle.test.js index fd0fbea3..f59973a4 100644 --- a/src/form/components/Toggle/Toggle.test.js +++ b/src/form/components/Toggle/Toggle.test.js @@ -173,8 +173,9 @@ describe('Toggle', () => { const onChange = cy.stub().as('onChange'); cy.mount() .get(selectors.item) - .click() - .get('@onChange') + .click(); + + cy.get('@onChange') .should('be.called'); }); @@ -195,7 +196,10 @@ describe('Toggle', () => { ) .get(selectors.item) .eq(1) - .click() + .as('option2') + .click(); + + cy.get('@option2') .should('have.class', classes.active); }); }); diff --git a/src/form/formik/FormikEditableList/index.js b/src/form/formik/FormikEditableList/index.js index b051728e..a311e5ad 100644 --- a/src/form/formik/FormikEditableList/index.js +++ b/src/form/formik/FormikEditableList/index.js @@ -2,5 +2,5 @@ import wrapper from '../../../utils/formik'; import EditableList from '../../components/EditableList'; export default wrapper(EditableList, { - getOnChangeProps: value => ({ value: value || [] }), + getOnChangeProps: (value) => ({ value: value || [] }), }); diff --git a/src/form/redux-form/FieldCodeEditor/index.js b/src/form/redux-form/FieldCodeEditor/index.js index 8af0f901..e0443981 100644 --- a/src/form/redux-form/FieldCodeEditor/index.js +++ b/src/form/redux-form/FieldCodeEditor/index.js @@ -4,19 +4,20 @@ import { fromJS } from 'immutable'; import CodeEditor from '../../components/CodeEditor'; -export default class FieldCodeEditor extends Component { - static propTypes = { - input: PropTypes.instanceOf(Object), - meta: PropTypes.instanceOf(Object), - validationmessages: PropTypes.instanceOf(Object), - }; - +class FieldCodeEditor extends Component { constructor(props) { super(props); this.handleOnChange = this.handleOnChange.bind(this); } + handleOnChange(value) { + const { input } = this.prop; + const { onChange } = input; + + onChange(fromJS(value)); + } + getInputError() { const { meta, validationmessages } = this.props; const { touched, error, warning } = meta; @@ -31,13 +32,6 @@ export default class FieldCodeEditor extends Component { return message; } - handleOnChange(value) { - const { input } = this.prop; - const { onChange } = input; - - onChange(fromJS(value)); - } - render() { const { input, ...rest } = this.props; @@ -50,3 +44,11 @@ export default class FieldCodeEditor extends Component { ); } } + +FieldCodeEditor.propTypes = { + input: PropTypes.instanceOf(Object), + meta: PropTypes.instanceOf(Object), + validationmessages: PropTypes.instanceOf(Object), +}; + +export default FieldCodeEditor; diff --git a/src/form/redux-form/FieldCombobox/index.js b/src/form/redux-form/FieldCombobox/index.js index 5817c9b2..8dbec2a7 100644 --- a/src/form/redux-form/FieldCombobox/index.js +++ b/src/form/redux-form/FieldCombobox/index.js @@ -4,19 +4,20 @@ import { fromJS } from 'immutable'; import Combobox from '../../components/Combobox'; -export default class FieldCombobox extends Component { - static propTypes = { - meta: PropTypes.instanceOf(Object), - input: PropTypes.instanceOf(Object), - validationmessages: PropTypes.instanceOf(Object), - } - +class FieldCombobox extends Component { constructor(props) { super(props); this.handleOnChange = this.handleOnChange.bind(this); } + handleOnChange(value) { + const { input } = this.props; + const { onChange } = input; + + onChange(fromJS(value)); + } + getComboboxError() { const { meta, validationmessages } = this.props; const { touched, error, warning } = meta; @@ -31,13 +32,6 @@ export default class FieldCombobox extends Component { return message; } - handleOnChange(value) { - const { input } = this.props; - const { onChange } = input; - - onChange(fromJS(value)); - } - render() { const { ...props } = this.props; @@ -51,3 +45,11 @@ export default class FieldCombobox extends Component { ); } } + +FieldCombobox.propTypes = { + meta: PropTypes.instanceOf(Object), + input: PropTypes.instanceOf(Object), + validationmessages: PropTypes.instanceOf(Object), +}; + +export default FieldCombobox; diff --git a/src/form/redux-form/FieldCombobox2/index.js b/src/form/redux-form/FieldCombobox2/index.js index 4838b4dd..75479e8a 100644 --- a/src/form/redux-form/FieldCombobox2/index.js +++ b/src/form/redux-form/FieldCombobox2/index.js @@ -4,7 +4,7 @@ import { fromJS } from 'immutable'; import Combobox2 from '../../components/Combobox2'; -const FieldCombobox2 = (props) => { +function FieldCombobox2(props) { const { input, meta, validationmessages } = props; const getComboboxError = () => { @@ -32,7 +32,7 @@ const FieldCombobox2 = (props) => { error={getComboboxError()} /> ); -}; +} FieldCombobox2.propTypes = { meta: PropTypes.instanceOf(Object), diff --git a/src/form/redux-form/FieldDatePicker/index.js b/src/form/redux-form/FieldDatePicker/index.js index 16250fb8..ea81d6c4 100644 --- a/src/form/redux-form/FieldDatePicker/index.js +++ b/src/form/redux-form/FieldDatePicker/index.js @@ -4,7 +4,7 @@ import { fromJS } from 'immutable'; import DatePicker from '../../components/DatePicker'; -const FormDatePicker = (props) => { +function FormDatePicker(props) { const { input, meta, @@ -37,7 +37,7 @@ const FormDatePicker = (props) => { error={getDatePickerError()} /> ); -}; +} FormDatePicker.propTypes = { meta: PropTypes.instanceOf(Object), diff --git a/src/form/redux-form/FieldDropdown/index.js b/src/form/redux-form/FieldDropdown/index.js index ebdf02d4..c70b8e94 100644 --- a/src/form/redux-form/FieldDropdown/index.js +++ b/src/form/redux-form/FieldDropdown/index.js @@ -4,17 +4,7 @@ import { fromJS } from 'immutable'; import Dropdown from '../../components/Dropdown'; -export default class FieldDropdown extends Component { - static propTypes = { - children: PropTypes.oneOfType([ - PropTypes.arrayOf(PropTypes.node), - PropTypes.node, - PropTypes.element, - PropTypes.string, - ]), - input: PropTypes.instanceOf(Object), - }; - +class FieldDropdown extends Component { constructor(props) { super(props); @@ -46,3 +36,15 @@ export default class FieldDropdown extends Component { ); } } + +FieldDropdown.propTypes = { + children: PropTypes.oneOfType([ + PropTypes.arrayOf(PropTypes.node), + PropTypes.node, + PropTypes.element, + PropTypes.string, + ]), + input: PropTypes.instanceOf(Object), +}; + +export default FieldDropdown; diff --git a/src/form/redux-form/FieldEditableList/index.js b/src/form/redux-form/FieldEditableList/index.js index 5c8ab90c..a9d96496 100644 --- a/src/form/redux-form/FieldEditableList/index.js +++ b/src/form/redux-form/FieldEditableList/index.js @@ -4,19 +4,20 @@ import { fromJS } from 'immutable'; import EditableList from '../../components/EditableList'; -export default class FieldEditableList extends Component { - static propTypes = { - input: PropTypes.instanceOf(Object), - meta: PropTypes.instanceOf(Object), - validationmessages: PropTypes.instanceOf(Object), - }; - +class FieldEditableList extends Component { constructor(props) { super(props); this.handleOnChange = this.handleOnChange.bind(this); } + handleOnChange(value) { + const { input } = this.props; + const { onChange } = input; + + onChange(fromJS(value)); + } + getEditableListError() { const { meta, validationmessages } = this.props; const { touched, error } = meta; @@ -29,13 +30,6 @@ export default class FieldEditableList extends Component { return message; } - handleOnChange(value) { - const { input } = this.props; - const { onChange } = input; - - onChange(fromJS(value)); - } - render() { const { ...props } = this.props; @@ -49,3 +43,11 @@ export default class FieldEditableList extends Component { ); } } + +FieldEditableList.propTypes = { + input: PropTypes.instanceOf(Object), + meta: PropTypes.instanceOf(Object), + validationmessages: PropTypes.instanceOf(Object), +}; + +export default FieldEditableList; diff --git a/src/form/redux-form/FieldFileInput/index.js b/src/form/redux-form/FieldFileInput/index.js index 98fe6e03..b37d99f5 100644 --- a/src/form/redux-form/FieldFileInput/index.js +++ b/src/form/redux-form/FieldFileInput/index.js @@ -3,13 +3,7 @@ import PropTypes from 'prop-types'; import FileInput from '../../components/FileInput'; -export default class FieldFileInput extends Component { - static propTypes = { - input: PropTypes.instanceOf(Object), - meta: PropTypes.instanceOf(Object), - validationmessages: PropTypes.instanceOf(Object), - }; - +class FieldFileInput extends Component { getInputError() { const { meta, validationmessages } = this.props; const { touched, error, warning } = meta; @@ -36,3 +30,11 @@ export default class FieldFileInput extends Component { ); } } + +FieldFileInput.propTypes = { + input: PropTypes.instanceOf(Object), + meta: PropTypes.instanceOf(Object), + validationmessages: PropTypes.instanceOf(Object), +}; + +export default FieldFileInput; diff --git a/src/form/redux-form/FieldInput/index.js b/src/form/redux-form/FieldInput/index.js index 2b9214de..be252e10 100644 --- a/src/form/redux-form/FieldInput/index.js +++ b/src/form/redux-form/FieldInput/index.js @@ -3,13 +3,7 @@ import PropTypes from 'prop-types'; import Input from '../../components/Input'; -export default class FieldInput extends Component { - static propTypes = { - meta: PropTypes.instanceOf(Object), - input: PropTypes.instanceOf(Object), - validationmessages: PropTypes.instanceOf(Object), - } - +class FieldInput extends Component { getInputError() { const { meta, validationmessages } = this.props; const { touched, error, warning } = meta; @@ -37,3 +31,11 @@ export default class FieldInput extends Component { ); } } + +FieldInput.propTypes = { + meta: PropTypes.instanceOf(Object), + input: PropTypes.instanceOf(Object), + validationmessages: PropTypes.instanceOf(Object), +}; + +export default FieldInput; diff --git a/src/form/redux-form/FieldMultiselect/index.js b/src/form/redux-form/FieldMultiselect/index.js index 1bee875b..b19d25c2 100644 --- a/src/form/redux-form/FieldMultiselect/index.js +++ b/src/form/redux-form/FieldMultiselect/index.js @@ -4,19 +4,20 @@ import { fromJS } from 'immutable'; import Multiselect from '../../components/Multiselect'; -export default class FieldMultiselect extends Component { - static propTypes = { - input: PropTypes.instanceOf(Object), - meta: PropTypes.instanceOf(Object), - validationmessages: PropTypes.instanceOf(Object), - } - +class FieldMultiselect extends Component { constructor(props) { super(props); this.handleOnChange = this.handleOnChange.bind(this); } + handleOnChange(value) { + const { input } = this.props; + const { onChange } = input; + + onChange(fromJS(value)); + } + getMultiselectError() { const { meta, validationmessages } = this.props; const { touched, error, warning } = meta; @@ -31,13 +32,6 @@ export default class FieldMultiselect extends Component { return message; } - handleOnChange(value) { - const { input } = this.props; - const { onChange } = input; - - onChange(fromJS(value)); - } - render() { const { ...props } = this.props; @@ -51,3 +45,11 @@ export default class FieldMultiselect extends Component { ); } } + +FieldMultiselect.propTypes = { + input: PropTypes.instanceOf(Object), + meta: PropTypes.instanceOf(Object), + validationmessages: PropTypes.instanceOf(Object), +}; + +export default FieldMultiselect; diff --git a/src/form/redux-form/FieldPagination/index.js b/src/form/redux-form/FieldPagination/index.js index 7c090fda..66880754 100644 --- a/src/form/redux-form/FieldPagination/index.js +++ b/src/form/redux-form/FieldPagination/index.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import Pagination from '../../components/Pagination'; -const FieldPagination = (props) => { +function FieldPagination(props) { const { input, ...rest } = props; return ( @@ -14,7 +14,7 @@ const FieldPagination = (props) => { value={input.value ? input.value : 0} /> ); -}; +} FieldPagination.propTypes = { input: PropTypes.instanceOf(Object), diff --git a/src/form/redux-form/FieldSelect/index.js b/src/form/redux-form/FieldSelect/index.js index f03c1e73..2ecfd9f6 100644 --- a/src/form/redux-form/FieldSelect/index.js +++ b/src/form/redux-form/FieldSelect/index.js @@ -4,19 +4,20 @@ import { fromJS } from 'immutable'; import Select from '../../components/Select'; -export default class FieldSelect extends Component { - static propTypes = { - input: PropTypes.instanceOf(Object), - meta: PropTypes.instanceOf(Object), - validationmessages: PropTypes.instanceOf(Object), - }; - +class FieldSelect extends Component { constructor(props) { super(props); this.handleEvent = this.handleEvent.bind(this); } + handleEvent(selectValue) { + const { input } = this.props; + const { onChange, value } = input; + + onChange(fromJS(selectValue !== undefined ? selectValue : value)); + } + getSelectError() { const { meta, validationmessages } = this.props; const { touched, error, warning } = meta; @@ -31,13 +32,6 @@ export default class FieldSelect extends Component { return message; } - handleEvent(selectValue) { - const { input } = this.props; - const { onChange, value } = input; - - onChange(fromJS(selectValue !== undefined ? selectValue : value)); - } - render() { const { input, ...rest } = this.props; @@ -54,3 +48,11 @@ export default class FieldSelect extends Component { ); } } + +FieldSelect.propTypes = { + input: PropTypes.instanceOf(Object), + meta: PropTypes.instanceOf(Object), + validationmessages: PropTypes.instanceOf(Object), +}; + +export default FieldSelect; diff --git a/src/form/redux-form/FieldSelectableList/index.js b/src/form/redux-form/FieldSelectableList/index.js index 30dac161..4a7bce8b 100644 --- a/src/form/redux-form/FieldSelectableList/index.js +++ b/src/form/redux-form/FieldSelectableList/index.js @@ -4,15 +4,7 @@ import { fromJS } from 'immutable'; import SelectableList from '../../components/SelectableList'; -export default class FieldSelectableList extends Component { - static propTypes = { - children: PropTypes.oneOfType([ - PropTypes.node, - ]), - onChange: PropTypes.func, - input: PropTypes.instanceOf(Object), - }; - +class FieldSelectableList extends Component { constructor(props) { super(props); @@ -37,3 +29,13 @@ export default class FieldSelectableList extends Component { ); } } + +FieldSelectableList.propTypes = { + children: PropTypes.oneOfType([ + PropTypes.node, + ]), + onChange: PropTypes.func, + input: PropTypes.instanceOf(Object), +}; + +export default FieldSelectableList; diff --git a/src/form/redux-form/FieldToggle/index.js b/src/form/redux-form/FieldToggle/index.js index e8115b43..c649f2b6 100644 --- a/src/form/redux-form/FieldToggle/index.js +++ b/src/form/redux-form/FieldToggle/index.js @@ -4,15 +4,7 @@ import { fromJS } from 'immutable'; import Toggle from '../../components/Toggle'; -export default class FieldToggle extends Component { - static propTypes = { - children: PropTypes.oneOfType([ - PropTypes.node, - ]), - onChange: PropTypes.func, - input: PropTypes.instanceOf(Object), - }; - +class FieldToggle extends Component { constructor(props) { super(props); @@ -39,3 +31,13 @@ export default class FieldToggle extends Component { ); } } + +FieldToggle.propTypes = { + children: PropTypes.oneOfType([ + PropTypes.node, + ]), + onChange: PropTypes.func, + input: PropTypes.instanceOf(Object), +}; + +export default FieldToggle; diff --git a/src/hooks/useTimeout/index.js b/src/hooks/useTimeout/index.js index 451cc2e3..6d3d4417 100644 --- a/src/hooks/useTimeout/index.js +++ b/src/hooks/useTimeout/index.js @@ -3,23 +3,27 @@ import { useEffect, useRef } from 'react'; const useTimeout = (callback, delay) => { const savedCallback = useRef(); - useEffect(() => { - savedCallback.current = callback; - }, - [callback]); + useEffect( + () => { + savedCallback.current = callback; + }, + [callback], + ); - useEffect(() => { - function tick() { - savedCallback.current(); - } - if (delay !== null) { - const id = setTimeout(tick, delay); - return () => clearTimeout(id); - } + useEffect( + () => { + function tick() { + savedCallback.current(); + } + if (delay !== null) { + const id = setTimeout(tick, delay); + return () => clearTimeout(id); + } - return () => {}; - }, - [delay]); + return () => {}; + }, + [delay], + ); }; /** @component */