diff --git a/package.json b/package.json index 7279ff4ee..978b3076f 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ }, "dependencies": { "@rainbow-modules/hooks": "^0.12.0", + "@rainbow-modules/validation": "^0.53.0", "autosize": "^4.0.2", "chart.js": "2.9.4", "clipboard-copy": "^2.0.0", @@ -134,7 +135,10 @@ "@rainbow-modules/icons": "^0.24.1-canary.2", "@rainbow-modules/search": "^0.51.1", "@stripe/stripe-js": "^1.3.0", + "@testing-library/jest-dom": "^5.16.5", + "@testing-library/react": "^12.1.5", "@testing-library/react-hooks": "^3.2.1", + "@testing-library/user-event": "^14.4.3", "@wdio/allure-reporter": "^7", "@wdio/cli": "^7", "@wdio/jasmine-framework": "^7", diff --git a/setupTests.js b/setupTests.js index e227221e9..34bff214c 100644 --- a/setupTests.js +++ b/setupTests.js @@ -3,6 +3,7 @@ import { configure } from 'enzyme'; import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; import { toHaveNoViolations } from 'jest-axe'; import toBeFocusable from './jestMatchers/toBeFocusable'; +import '@testing-library/jest-dom'; configure({ adapter: new Adapter() }); diff --git a/src/components/Button/readme.md b/src/components/Button/readme.md index 72da2e115..743c0f692 100644 --- a/src/components/Button/readme.md +++ b/src/components/Button/readme.md @@ -174,8 +174,8 @@ import React from 'react'; import { Button } from 'react-rainbow-components';
-
``` diff --git a/src/components/ButtonIcon/index.d.ts b/src/components/ButtonIcon/index.d.ts index 469e6d3b0..d45708e00 100644 --- a/src/components/ButtonIcon/index.d.ts +++ b/src/components/ButtonIcon/index.d.ts @@ -25,6 +25,7 @@ export interface ButtonIconProps extends BaseProps { ariaPressed?: boolean; form?: string; id?: string; + borderRadius?: 'square' | 'semi-rounded' | 'rounded'; } declare const ButtonIcon: ComponentType; diff --git a/src/components/ButtonIcon/index.js b/src/components/ButtonIcon/index.js index 19385f01a..57193f1d4 100644 --- a/src/components/ButtonIcon/index.js +++ b/src/components/ButtonIcon/index.js @@ -61,6 +61,7 @@ const ButtonIcon = React.forwardRef((props, ref) => { variant, size, tooltip, + borderRadius, } = props; const { onMouseEnter, @@ -119,6 +120,7 @@ const ButtonIcon = React.forwardRef((props, ref) => { onMouseLeave={handleMouseLeave} form={form} ref={buttonRef} + borderRadius={borderRadius} > {icon} @@ -209,6 +211,8 @@ ButtonIcon.propTypes = { style: PropTypes.object, /** The id of the outer element. */ id: PropTypes.string, + /** The border radius of the button. Valid values are square, semi-rounded and rounded. This value defaults to rounded. */ + borderRadius: PropTypes.oneOf(['square', 'semi-rounded', 'rounded']), }; ButtonIcon.defaultProps = { @@ -237,6 +241,7 @@ ButtonIcon.defaultProps = { ariaControls: undefined, ariaExpanded: undefined, form: undefined, + borderRadius: 'rounded', }; export default ButtonIcon; diff --git a/src/components/ButtonIcon/readme.md b/src/components/ButtonIcon/readme.md index 0ab4a3b69..a8d9510d7 100644 --- a/src/components/ButtonIcon/readme.md +++ b/src/components/ButtonIcon/readme.md @@ -33,6 +33,28 @@ import { faStar } from '@fortawesome/free-regular-svg-icons'; ``` +# ButtonIcons with Border Radius +##### ButtonIcons with different border radius. + +```js +import React from 'react'; +import { ButtonIcon } from 'react-rainbow-components'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faStar } from '@fortawesome/free-regular-svg-icons'; + +
+
+ } /> +
+
+ } /> +
+
+ } /> +
+
+``` + # ButtonIcons with shaded variant ##### The appearance of a ButtonIcon can be changed by implementing the shaded variant, so the whole section will appear with a shadow around it. diff --git a/src/components/ButtonIcon/styled/button.js b/src/components/ButtonIcon/styled/button.js index 21b987021..25d47d4b3 100644 --- a/src/components/ButtonIcon/styled/button.js +++ b/src/components/ButtonIcon/styled/button.js @@ -1,5 +1,9 @@ import styled from 'styled-components'; -import { BORDER_RADIUS_2 } from '../../../styles/borderRadius'; +import { + BORDER_RADIUS_2, + BORDER_RADIUS_SQUARE, + BORDER_RADIUS_SEMI_ROUNDED, +} from '../../../styles/borderRadius'; import { COLOR_WHITE, COLOR_GRAY_3, COLOR_DARK_1 } from '../../../styles/colors'; import { lighten, colorToRgba, replaceAlpha } from '../../../styles/helpers/color'; import attachThemeAttrs from '../../../styles/helpers/attachThemeAttrs'; @@ -397,6 +401,18 @@ const StyledButton = attachThemeAttrs(styled.button).attrs(props => { font-size: 1rem !important; } `}; + + ${props => + props.borderRadius === 'square' && + ` + border-radius: ${BORDER_RADIUS_SQUARE}; + `}; + + ${props => + props.borderRadius === 'semi-rounded' && + ` + border-radius: ${BORDER_RADIUS_SEMI_ROUNDED}; + `}; `; export default StyledButton; diff --git a/src/components/CurrencyInput/__test__/currencyInput.spec.js b/src/components/CurrencyInput/__test__/currencyInput.spec.js new file mode 100644 index 000000000..780558f71 --- /dev/null +++ b/src/components/CurrencyInput/__test__/currencyInput.spec.js @@ -0,0 +1,145 @@ +import React, { useState } from 'react'; +import { render, screen, fireEvent } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import CurrencyInput from '..'; + +const TestCurrencyInput = props => { + // eslint-disable-next-line react/prop-types + const { initialValue } = props; + const [value, setValue] = useState(initialValue); + return ; +}; + +describe('', () => { + it('should mount a input with a value of $5.00', () => { + [5, '5', '5.', '5.00', '5.00003'].forEach(value => { + render(); + }); + screen.getAllByRole('textbox').forEach(input => { + expect(input).toHaveValue('$5.00'); + }); + }); + + it('should mount a input with a value of $0.00', () => { + [0, '.0', '0.00003'].forEach(value => { + render(); + }); + screen.getAllByRole('textbox').forEach(input => { + expect(input).toHaveValue('$0.00'); + }); + }); + + it('should mount a input with a empty value', () => { + ['', ' ', 'foo', null, undefined, true, false, Infinity, {}, []].forEach(value => { + render(); + }); + screen.getAllByRole('textbox').forEach(input => { + expect(input).toHaveValue(''); + }); + }); + + it('should mount a input with a value of "-"', () => { + const { getByRole } = render(); + expect(getByRole('textbox')).toHaveValue('-'); + }); + + it('should be change value to 5 when is fucused', () => { + const { getByRole } = render(); + const input = getByRole('textbox'); + expect(input).toHaveValue('$5.00'); + fireEvent.focus(input); + expect(input).toHaveValue('5'); + }); + + it('should be change value to $5.00 when is not focused', () => { + const { getByRole } = render(); + const input = getByRole('textbox'); + fireEvent.focus(input); + expect(input).toHaveValue('5'); + fireEvent.blur(input); + expect(input).toHaveValue('$5.00'); + }); + + it('should fire onChange with the value when it is a valid value', () => { + const onChangeMockFn = jest.fn(); + const { getByRole } = render(); + const input = getByRole('textbox'); + ['5', '5.', '05', '5.00', '-', '.0', '-5'].forEach(value => { + fireEvent.change(input, { target: { value } }); + expect(onChangeMockFn).toHaveBeenCalledWith(value); + }); + }); + + it('should not fire onChange with the value when it is an invalid value', () => { + const onChangeMockFn = jest.fn(); + const { getByRole } = render(); + const input = getByRole('textbox'); + ['5..', '5..0', '.', 'foo', ' ', ''].forEach(value => { + fireEvent.change(input, { target: { value } }); + }); + expect(onChangeMockFn).toHaveBeenCalledTimes(0); + }); + + it('should calculate the selectionStart when writing to the input', async () => { + const { getByRole } = render(); + const input = getByRole('textbox'); + + fireEvent.focus(input); + input.focus(); + input.setSelectionRange(0, 0); + expect(input.selectionStart).toBe(0); + expect(input.selectionEnd).toBe(0); + + await userEvent.type(input, '123'); + expect(input).toHaveValue('123'); + expect(input.selectionStart).toBe(3); + + await userEvent.type(input, '4'); + expect(input).toHaveValue('1,234'); + expect(input.selectionStart).toBe(5); + }); + + it('should calculate the selectionStart when a valid number is pasted into the input', async () => { + const { getByRole } = render(); + const input = getByRole('textbox'); + + fireEvent.focus(input); + input.focus(); + input.setSelectionRange(2, 2); + expect(input.selectionStart).toBe(2); + expect(input).toHaveValue('1,234'); + + await userEvent.paste('567'); + expect(input).toHaveValue('1,567,234'); + expect(input.selectionStart).toBe(6); + }); + + it('should calculate the selectionStart when an input slice is cut', async () => { + const { getByRole } = render(); + const input = getByRole('textbox'); + + fireEvent.focus(input); + input.focus(); + input.setSelectionRange(2, 6); + expect(input).toHaveValue('1,234,567'); + + expect(input.selectionStart).toBe(2); + await userEvent.cut(); + expect(input).toHaveValue('1,567'); + expect(input.selectionStart).toBe(1); + }); + + it('should calculate the selectionStart when an input slice is cut', async () => { + const { getByRole } = render(); + const input = getByRole('textbox'); + + fireEvent.focus(input); + expect(input).toHaveValue('0.0'); + + expect(input.selectionStart).toBe(0); + fireEvent.change(input, { target: { value: '01.0', selectionStart: 2 } }); + expect(input).toHaveValue('1.0'); + expect(input.selectionStart).toBe(1); + }); +}); diff --git a/src/components/CurrencyInput/helpers/formatCurrency.js b/src/components/CurrencyInput/helpers/formatCurrency.js index 41ffad077..d2b95b6ab 100644 --- a/src/components/CurrencyInput/helpers/formatCurrency.js +++ b/src/components/CurrencyInput/helpers/formatCurrency.js @@ -1,11 +1,12 @@ +import isValidNumberValue from './isValidNumberValue'; + export default function formatCurrency({ value, locale, options }) { - if (value === '-' || value === '' || value === null) { + if (value === '-') { return value; } - const number = Number(value); - if (!Number.isNaN(number)) { - return new Intl.NumberFormat(locale, options).format(number); + if (isValidNumberValue(value)) { + return new Intl.NumberFormat(locale, options).format(Number(value)); } return ''; diff --git a/src/components/CurrencyInput/helpers/index.js b/src/components/CurrencyInput/helpers/index.js index 37c70e394..bf1119f94 100644 --- a/src/components/CurrencyInput/helpers/index.js +++ b/src/components/CurrencyInput/helpers/index.js @@ -5,3 +5,4 @@ export { default as countCharacters } from './countCharacters'; export { default as isValidStringNumber } from './isValidStringNumber'; export { default as normalizeValue } from './normalizeValue'; export { default as formatInteger } from './formatInteger'; +export { default as isValidNumberValue } from './isValidNumberValue'; diff --git a/src/components/CurrencyInput/helpers/isValidNumberValue.js b/src/components/CurrencyInput/helpers/isValidNumberValue.js new file mode 100644 index 000000000..7769748e5 --- /dev/null +++ b/src/components/CurrencyInput/helpers/isValidNumberValue.js @@ -0,0 +1,6 @@ +import { isBoolean, isEmpty, isNumber } from '@rainbow-modules/validation'; + +export default function isValidNumberValue(value) { + const number = Number(value); + return !isBoolean(value) && !isEmpty(value) && isNumber(number) && Number.isFinite(number); +} diff --git a/src/components/CurrencyInput/helpers/normalizeValue.js b/src/components/CurrencyInput/helpers/normalizeValue.js index 5c0aa5590..a3de51232 100644 --- a/src/components/CurrencyInput/helpers/normalizeValue.js +++ b/src/components/CurrencyInput/helpers/normalizeValue.js @@ -1,12 +1,13 @@ import formatInteger from './formatInteger'; +import isValidNumberValue from './isValidNumberValue'; export default function normalizeValue({ value, locale, decimalSeparator, options }) { const stringValue = String(value); - if (value === '-' || value === '' || value === null) { + if (value === '-') { return value; } - if (!Number.isNaN(Number(value))) { + if (isValidNumberValue(value)) { const [integer, fraction] = stringValue.split('.'); const formattedInteger = formatInteger({ integer, locale, options }); diff --git a/src/components/PercentInput/__test__/percentInput.spec.js b/src/components/PercentInput/__test__/percentInput.spec.js new file mode 100644 index 000000000..615e47882 --- /dev/null +++ b/src/components/PercentInput/__test__/percentInput.spec.js @@ -0,0 +1,145 @@ +import React, { useState } from 'react'; +import { render, screen, fireEvent } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import PercentInput from '..'; + +const TestPercentInput = props => { + // eslint-disable-next-line react/prop-types + const { initialValue } = props; + const [value, setValue] = useState(initialValue); + return ; +}; + +describe('', () => { + it('should mount a input with a value of 5%', () => { + [5, '5', '5.', '5.00', '5.00003'].forEach(value => { + render(); + }); + screen.getAllByRole('textbox').forEach(input => { + expect(input).toHaveValue('5%'); + }); + }); + + it('should mount a input with a value of 0%', () => { + [0, '.0', '0.00003'].forEach(value => { + render(); + }); + screen.getAllByRole('textbox').forEach(input => { + expect(input).toHaveValue('0%'); + }); + }); + + it('should mount a input with a empty value', () => { + ['', ' ', 'foo', null, undefined, true, false, Infinity, {}, []].forEach(value => { + render(); + }); + screen.getAllByRole('textbox').forEach(input => { + expect(input).toHaveValue(''); + }); + }); + + it('should mount a input with a value of "-"', () => { + const { getByRole } = render(); + expect(getByRole('textbox')).toHaveValue('-'); + }); + + it('should be change value to 5 when is fucused', () => { + const { getByRole } = render(); + const input = getByRole('textbox'); + expect(input).toHaveValue('5%'); + fireEvent.focus(input); + expect(input).toHaveValue('5'); + }); + + it('should be change value to $5.00 when is not focused', () => { + const { getByRole } = render(); + const input = getByRole('textbox'); + fireEvent.focus(input); + expect(input).toHaveValue('5'); + fireEvent.blur(input); + expect(input).toHaveValue('5%'); + }); + + it('should fire onChange with the value when it is a valid value', () => { + const onChangeMockFn = jest.fn(); + const { getByRole } = render(); + const input = getByRole('textbox'); + ['5', '5.', '05', '5.00', '-', '.0', '-5'].forEach(value => { + fireEvent.change(input, { target: { value } }); + expect(onChangeMockFn).toHaveBeenCalledWith(value); + }); + }); + + it('should not fire onChange with the value when it is an invalid value', () => { + const onChangeMockFn = jest.fn(); + const { getByRole } = render(); + const input = getByRole('textbox'); + ['5..', '5..0', '.', 'foo', ' ', ''].forEach(value => { + fireEvent.change(input, { target: { value } }); + }); + expect(onChangeMockFn).toHaveBeenCalledTimes(0); + }); + + it('should calculate the selectionStart when writing to the input', async () => { + const { getByRole } = render(); + const input = getByRole('textbox'); + + fireEvent.focus(input); + input.focus(); + input.setSelectionRange(0, 0); + expect(input.selectionStart).toBe(0); + expect(input.selectionEnd).toBe(0); + + await userEvent.type(input, '123'); + expect(input).toHaveValue('123'); + expect(input.selectionStart).toBe(3); + + await userEvent.type(input, '4'); + expect(input).toHaveValue('1,234'); + expect(input.selectionStart).toBe(5); + }); + + it('should calculate the selectionStart when a valid number is pasted into the input', async () => { + const { getByRole } = render(); + const input = getByRole('textbox'); + + fireEvent.focus(input); + input.focus(); + input.setSelectionRange(2, 2); + expect(input.selectionStart).toBe(2); + expect(input).toHaveValue('1,234'); + + await userEvent.paste('567'); + expect(input).toHaveValue('1,567,234'); + expect(input.selectionStart).toBe(6); + }); + + it('should calculate the selectionStart when an input slice is cut', async () => { + const { getByRole } = render(); + const input = getByRole('textbox'); + + fireEvent.focus(input); + input.focus(); + input.setSelectionRange(2, 6); + expect(input).toHaveValue('1,234,567'); + + expect(input.selectionStart).toBe(2); + await userEvent.cut(); + expect(input).toHaveValue('1,567'); + expect(input.selectionStart).toBe(1); + }); + + it('should calculate the selectionStart when an input slice is cut', async () => { + const { getByRole } = render(); + const input = getByRole('textbox'); + + fireEvent.focus(input); + expect(input).toHaveValue('0.0'); + + expect(input.selectionStart).toBe(0); + fireEvent.change(input, { target: { value: '01.0', selectionStart: 2 } }); + expect(input).toHaveValue('1.0'); + expect(input.selectionStart).toBe(1); + }); +}); diff --git a/src/components/PercentInput/helpers/formatPercent.js b/src/components/PercentInput/helpers/formatPercent.js index 339a206c0..f90777583 100644 --- a/src/components/PercentInput/helpers/formatPercent.js +++ b/src/components/PercentInput/helpers/formatPercent.js @@ -1,11 +1,11 @@ +import { isValidNumberValue } from '../../CurrencyInput/helpers'; + export default function formatPercent({ value, locale, options }) { - if (value === '-' || value === '' || value === null) { + if (value === '-') { return value; } - const number = Number(value); - - if (!Number.isNaN(number)) { - return new Intl.NumberFormat(locale, options).format(number / 100); + if (isValidNumberValue(value)) { + return new Intl.NumberFormat(locale, options).format(Number(value) / 100); } return ''; diff --git a/src/components/PercentInput/helpers/normalizeValue.js b/src/components/PercentInput/helpers/normalizeValue.js index 7b5be7d7b..22a7a0b24 100644 --- a/src/components/PercentInput/helpers/normalizeValue.js +++ b/src/components/PercentInput/helpers/normalizeValue.js @@ -1,12 +1,13 @@ import formatInteger from '../../CurrencyInput/helpers/formatInteger'; +import { isValidNumberValue } from '../../CurrencyInput/helpers'; export default function normalizeValue({ value, locale, decimalSeparator, options }) { const stringValue = String(value); - if (value === '-' || value === '' || value === null) { + if (value === '-') { return value; } - if (!Number.isNaN(Number(value))) { + if (isValidNumberValue(value)) { const [integer, fraction] = stringValue.split('.'); const formattedInteger = formatInteger({ integer: Number(integer) / 100, locale, options }); diff --git a/yarn.lock b/yarn.lock index 014a31998..f8b71d07b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,11 @@ # yarn lockfile v1 +"@adobe/css-tools@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.0.1.tgz#b38b444ad3aa5fedbb15f2f746dcd934226a12dd" + integrity sha512-+u76oB43nOHrF4DDWRLWDCtci7f3QJoEBigemIdIeTi1ODqjx6Tad9NCVnPRwewWlKkVab5PlK8DCtPTyX7S8g== + "@algolia/cache-browser-local-storage@4.13.1": version "4.13.1" resolved "https://registry.yarnpkg.com/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.13.1.tgz#ffacb9230119f77de1a6f163b83680be999110e4" @@ -1504,6 +1509,13 @@ dependencies: jest-get-type "^28.0.2" +"@jest/expect-utils@^29.2.0": + version "29.2.0" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.2.0.tgz#3c0c472115d98211e7e0a0a8fa00719bf081987f" + integrity sha512-nz2IDF7nb1qmj9hx8Ja3MFab2q9Ml8QbOaaeJNyX5JQJHU8QUvEDiMctmhGEkk3Kzr8w8vAqz4hPk/ogJSrUhg== + dependencies: + jest-get-type "^29.2.0" + "@jest/fake-timers@^24.9.0": version "24.9.0" resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-24.9.0.tgz#ba3e6bf0eecd09a636049896434d306636540c93" @@ -1547,6 +1559,13 @@ dependencies: "@sinclair/typebox" "^0.23.3" +"@jest/schemas@^29.0.0": + version "29.0.0" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.0.0.tgz#5f47f5994dd4ef067fb7b4188ceac45f77fe952a" + integrity sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA== + dependencies: + "@sinclair/typebox" "^0.24.1" + "@jest/source-map@^24.3.0", "@jest/source-map@^24.9.0": version "24.9.0" resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-24.9.0.tgz#0e263a94430be4b41da683ccc1e6bffe2a191714" @@ -1649,6 +1668,18 @@ "@types/yargs" "^17.0.8" chalk "^4.0.0" +"@jest/types@^29.2.0": + version "29.2.0" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.2.0.tgz#c0d1ef8bc1e4f4b358e7877e34157371e7881b0b" + integrity sha512-mfgpQz4Z2xGo37m6KD8xEpKelaVzvYVRijmLPePn9pxgaPEtX+SqIyPNzzoeCPXKYbB4L/wYSgXDL8o3Gop78Q== + dependencies: + "@jest/schemas" "^29.0.0" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" + "@jridgewell/gen-mapping@^0.1.0": version "0.1.1" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996" @@ -1958,6 +1989,11 @@ resolved "https://registry.yarnpkg.com/@rainbow-modules/validation/-/validation-0.51.0.tgz#c228334e376aba36063ff3809defc0d8cca5ea76" integrity sha512-W51fS/AXsXjULau4YPJzHLXnl389bHNjU5Hy5jjILvssptaCxZBBVvasVhm2jbU+rRhDAEer4g9StobICGp3uA== +"@rainbow-modules/validation@^0.53.0": + version "0.53.0" + resolved "https://registry.yarnpkg.com/@rainbow-modules/validation/-/validation-0.53.0.tgz#2c81b3e5bb61bc2daa4506a6d33ef239b6ae50e9" + integrity sha512-NfnQv6jDk8xIeLyS2lAFK/sZ7VPfLPpCJnxjkj+Ib0XVqsdcc01CYcSdJUax2gR2B6TAoarU+E6KeEpA7aSyow== + "@sideway/address@^4.1.3": version "4.1.4" resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.4.tgz#03dccebc6ea47fdc226f7d3d1ad512955d4783f0" @@ -1980,6 +2016,11 @@ resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.23.5.tgz#93f7b9f4e3285a7a9ade7557d9a8d36809cbc47d" integrity sha512-AFBVi/iT4g20DHoujvMH1aEDn8fGJh4xsRGCP6d8RpLPMqsNPvW01Jcn0QysXTsg++/xj25NmJsGyH9xug/wKg== +"@sinclair/typebox@^0.24.1": + version "0.24.46" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.46.tgz#57501b58023776dbbae9e25619146286440be34c" + integrity sha512-ng4ut1z2MCBhK/NwDVwIQp3pAUOCs/KNaW3cBxdFB2xTDrOuo1xuNmpr/9HHFhxqIvHrs1NTH3KJg6q+JSy1Kw== + "@sindresorhus/is@^0.14.0": version "0.14.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" @@ -2042,6 +2083,35 @@ dependencies: defer-to-connect "^2.0.0" +"@testing-library/dom@^8.0.0": + version "8.19.0" + resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.19.0.tgz#bd3f83c217ebac16694329e413d9ad5fdcfd785f" + integrity sha512-6YWYPPpxG3e/xOo6HIWwB/58HukkwIVTOaZ0VwdMVjhRUX/01E4FtQbck9GazOOj7MXHc5RBzMrU86iBJHbI+A== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/runtime" "^7.12.5" + "@types/aria-query" "^4.2.0" + aria-query "^5.0.0" + chalk "^4.1.0" + dom-accessibility-api "^0.5.9" + lz-string "^1.4.4" + pretty-format "^27.0.2" + +"@testing-library/jest-dom@^5.16.5": + version "5.16.5" + resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.16.5.tgz#3912846af19a29b2dbf32a6ae9c31ef52580074e" + integrity sha512-N5ixQ2qKpi5OLYfwQmUb/5mSV9LneAcaUfp32pn4yCnpb8r/Yz0pXFPck21dIicKmi+ta5WRAknkZCfA8refMA== + dependencies: + "@adobe/css-tools" "^4.0.1" + "@babel/runtime" "^7.9.2" + "@types/testing-library__jest-dom" "^5.9.1" + aria-query "^5.0.0" + chalk "^3.0.0" + css.escape "^1.5.1" + dom-accessibility-api "^0.5.6" + lodash "^4.17.15" + redent "^3.0.0" + "@testing-library/react-hooks@^3.2.1": version "3.7.0" resolved "https://registry.yarnpkg.com/@testing-library/react-hooks/-/react-hooks-3.7.0.tgz#6d75c5255ef49bce39b6465bf6b49e2dac84919e" @@ -2050,6 +2120,20 @@ "@babel/runtime" "^7.12.5" "@types/testing-library__react-hooks" "^3.4.0" +"@testing-library/react@^12.1.5": + version "12.1.5" + resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-12.1.5.tgz#bb248f72f02a5ac9d949dea07279095fa577963b" + integrity sha512-OfTXCJUFgjd/digLUuPxa0+/3ZxsQmE7ub9kcbW/wi96Bh3o/p5vrETcBGfP17NWPGqeYYl5LTRpwyGoMC4ysg== + dependencies: + "@babel/runtime" "^7.12.5" + "@testing-library/dom" "^8.0.0" + "@types/react-dom" "<18.0.0" + +"@testing-library/user-event@^14.4.3": + version "14.4.3" + resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-14.4.3.tgz#af975e367743fa91989cd666666aec31a8f50591" + integrity sha512-kCUc5MEwaEMakkO5x7aoD+DLi02ehmEM2QCGWvNqAS1dV/fAvORWEjnjsEIvml59M7Y5kCkWN6fCCyPOe8OL6Q== + "@tootallnate/once@1": version "1.1.2" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" @@ -2081,6 +2165,11 @@ url-parse "^1.1.7" webfontloader "^1.6.27" +"@types/aria-query@^4.2.0": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.2.tgz#ed4e0ad92306a704f9fb132a0cfcf77486dbe2bc" + integrity sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig== + "@types/aria-query@^5.0.0": version "5.0.0" resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-5.0.0.tgz#df2d64b5cc73cca0d75e2a7793d6b5c199c2f7b2" @@ -2233,6 +2322,14 @@ resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-3.10.3.tgz#a89798b3d5a8bd23ca56e855a9aee3e5a93bdaaa" integrity sha512-SWyMrjgdAUHNQmutvDcKablrJhkDLy4wunTme8oYLjKp41GnHGxMRXr2MQMvy/qy8H3LdzwQk9gH4hZ6T++H8g== +"@types/jest@*": + version "29.1.2" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.1.2.tgz#7ad8077043ab5f6c108c8111bcc1d224e5600a87" + integrity sha512-y+nlX0h87U0R+wsGn6EBuoRWYyv3KFtwRNP3QWp9+k2tJ2/bqcGS3UxD7jgT+tiwJWWq3UsyV4Y+T6rsMT4XMg== + dependencies: + expect "^29.0.0" + pretty-format "^29.0.0" + "@types/json-buffer@~3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/json-buffer/-/json-buffer-3.0.0.tgz#85c1ff0f0948fc159810d4b5be35bf8c20875f64" @@ -2346,6 +2443,13 @@ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf" integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== +"@types/react-dom@<18.0.0": + version "17.0.17" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.17.tgz#2e3743277a793a96a99f1bf87614598289da68a1" + integrity sha512-VjnqEmqGnasQKV0CWLevqMTXBYG9GbwuE6x3VetERLh0cq2LTptFE73MrQi2S7GkKXCf2GgwItB/melLnxfnsg== + dependencies: + "@types/react" "^17" + "@types/react-redux@^7.1.20": version "7.1.24" resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.24.tgz#6caaff1603aba17b27d20f8ad073e4c077e975c0" @@ -2372,6 +2476,15 @@ "@types/scheduler" "*" csstype "^3.0.2" +"@types/react@^17": + version "17.0.50" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.50.tgz#39abb4f7098f546cfcd6b51207c90c4295ee81fc" + integrity sha512-ZCBHzpDb5skMnc1zFXAXnL3l1FAdi+xZvwxK+PkglMmBrwjpp9nKaWuEvrGnSifCJmBFGxZOOFuwC6KH/s0NuA== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + "@types/recursive-readdir@^2.2.0": version "2.2.1" resolved "https://registry.yarnpkg.com/@types/recursive-readdir/-/recursive-readdir-2.2.1.tgz#330f5ec0b73e8aeaf267a6e056884e393f3543a3" @@ -2420,6 +2533,13 @@ resolved "https://registry.yarnpkg.com/@types/supports-color/-/supports-color-8.1.1.tgz#1b44b1b096479273adf7f93c75fc4ecc40a61ee4" integrity sha512-dPWnWsf+kzIG140B8z2w3fr5D03TLWbOAFQl45xUpI3vcizeXriNR5VYkWZ+WTMsUHqZ9Xlt3hrxGNANFyNQfw== +"@types/testing-library__jest-dom@^5.9.1": + version "5.14.5" + resolved "https://registry.yarnpkg.com/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.5.tgz#d113709c90b3c75fdb127ec338dad7d5f86c974f" + integrity sha512-SBwbxYoyPIvxHbeHxTZX2Pe/74F/tX2/D3mMvzabdeJ25bBojfW0TyB8BHrbq/9zaaKICJZjLP+8r6AeZMFCuQ== + dependencies: + "@types/jest" "*" + "@types/testing-library__react-hooks@^3.4.0": version "3.4.1" resolved "https://registry.yarnpkg.com/@types/testing-library__react-hooks/-/testing-library__react-hooks-3.4.1.tgz#b8d7311c6c1f7db3103e94095fe901f8fef6e433" @@ -5578,6 +5698,11 @@ css-what@^6.1.0: resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== +css.escape@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb" + integrity sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg== + cssesc@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" @@ -6012,6 +6137,11 @@ diff-sequences@^28.1.1: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-28.1.1.tgz#9989dc731266dc2903457a70e996f3a041913ac6" integrity sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw== +diff-sequences@^29.2.0: + version "29.2.0" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.2.0.tgz#4c55b5b40706c7b5d2c5c75999a50c56d214e8f6" + integrity sha512-413SY5JpYeSBZxmenGEmCVQ8mCgtFJF0w9PROdaS6z987XC2Pd2GOKqOITLtMftmyFZqgtCOb/QA7/Z3ZXfzIw== + diff@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" @@ -6092,6 +6222,11 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" +dom-accessibility-api@^0.5.6, dom-accessibility-api@^0.5.9: + version "0.5.14" + resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.14.tgz#56082f71b1dc7aac69d83c4285eef39c15d93f56" + integrity sha512-NMt+m9zFMPZe0JcY9gN224Qvk6qLIdqex29clBvc/y75ZBX9YA9wNK3frsYvu2DI1xcCIwxwnX+TlsJ2DSOADg== + dom-helpers@^5.1.3: version "5.2.1" resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902" @@ -7045,6 +7180,17 @@ expect@^28.1.0: jest-message-util "^28.1.1" jest-util "^28.1.1" +expect@^29.0.0: + version "29.2.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.2.0.tgz#b90c6df52be7abfd9f206f273fbcf8b33d8f332d" + integrity sha512-03ClF3GWwUqd9Grgkr9ZSdaCJGMRA69PQ8jT7o+Bx100VlGiAFf9/8oIm9Qve7ZVJhuJxFftqFhviZJRxxNfvg== + dependencies: + "@jest/expect-utils" "^29.2.0" + jest-get-type "^29.2.0" + jest-matcher-utils "^29.2.0" + jest-message-util "^29.2.0" + jest-util "^29.2.0" + express@^4.16.4, express@^4.17.1: version "4.18.1" resolved "https://registry.yarnpkg.com/express/-/express-4.18.1.tgz#7797de8b9c72c857b9cd0e14a5eea80666267caf" @@ -9728,6 +9874,16 @@ jest-diff@^28.1.1: jest-get-type "^28.0.2" pretty-format "^28.1.1" +jest-diff@^29.2.0: + version "29.2.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.2.0.tgz#b1e11ac1a1401fc4792ef8ba406b48f1ae7d2bc5" + integrity sha512-GsH07qQL+/D/GxlnU+sSg9GL3fBOcuTlmtr3qr2pnkiODCwubNN2/7slW4m3CvxDsEus/VEOfQKRFLyXsUlnZw== + dependencies: + chalk "^4.0.0" + diff-sequences "^29.2.0" + jest-get-type "^29.2.0" + pretty-format "^29.2.0" + jest-docblock@^24.3.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-24.9.0.tgz#7970201802ba560e1c4092cc25cbedf5af5a8ce2" @@ -9784,6 +9940,11 @@ jest-get-type@^28.0.2: resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-28.0.2.tgz#34622e628e4fdcd793d46db8a242227901fcf203" integrity sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA== +jest-get-type@^29.2.0: + version "29.2.0" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.2.0.tgz#726646f927ef61d583a3b3adb1ab13f3a5036408" + integrity sha512-uXNJlg8hKFEnDgFsrCjznB+sTxdkuqiCL6zMgA75qEbAJjJYTs9XPrvDctrEig2GDow22T/LvHgO57iJhXB/UA== + jest-haste-map@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-24.9.0.tgz#b38a5d64274934e21fa417ae9a9fbeb77ceaac7d" @@ -9891,6 +10052,16 @@ jest-matcher-utils@^28.1.0, jest-matcher-utils@^28.1.1: jest-get-type "^28.0.2" pretty-format "^28.1.1" +jest-matcher-utils@^29.2.0: + version "29.2.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.2.0.tgz#d1d73add0e0efb0e316a50f296977505dc053e02" + integrity sha512-FcEfKZ4vm28yCdBsvC69EkrEhcfex+IYlRctNJXsRG9+WC3WxgBNORnECIgqUtj7o/h1d8o7xB/dFUiLi4bqtw== + dependencies: + chalk "^4.0.0" + jest-diff "^29.2.0" + jest-get-type "^29.2.0" + pretty-format "^29.2.0" + jest-message-util@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-24.9.0.tgz#527f54a1e380f5e202a8d1149b0ec872f43119e3" @@ -9934,6 +10105,21 @@ jest-message-util@^28.1.1: slash "^3.0.0" stack-utils "^2.0.3" +jest-message-util@^29.2.0: + version "29.2.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.2.0.tgz#cbd43fd9a20a8facd4267ac37556bc5c9a525ec0" + integrity sha512-arBfk5yMFMTnMB22GyG601xGSGthA02vWSewPaxoFo0F9wBqDOyxccPbCcYu8uibw3kduSHXdCOd1PsLSgdomg== + dependencies: + "@babel/code-frame" "^7.12.13" + "@jest/types" "^29.2.0" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^29.2.0" + slash "^3.0.0" + stack-utils "^2.0.3" + jest-mock@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-24.9.0.tgz#c22835541ee379b908673ad51087a2185c13f1c6" @@ -10090,6 +10276,18 @@ jest-util@^28.1.1: graceful-fs "^4.2.9" picomatch "^2.2.3" +jest-util@^29.2.0: + version "29.2.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.2.0.tgz#797935697e83a5722aeba401ed6cd01264295566" + integrity sha512-8M1dx12ujkBbnhwytrezWY0Ut79hbflwodE+qZKjxSRz5qt4xDp6dQQJaOCFvCmE0QJqp9KyEK33lpPNjnhevw== + dependencies: + "@jest/types" "^29.2.0" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + jest-validate@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-24.9.0.tgz#0775c55360d173cd854e40180756d4ff52def8ab" @@ -11165,6 +11363,11 @@ luxon@^2.3.0: resolved "https://registry.yarnpkg.com/luxon/-/luxon-2.4.0.tgz#9435806545bb32d4234dab766ab8a3d54847a765" integrity sha512-w+NAwWOUL5hO0SgwOHsMBAmZ15SoknmQXhSO0hIbJCAmPKSsGeK8MlmhYh2w6Iib38IxN2M+/ooXWLbeis7GuA== +lz-string@^1.4.4: + version "1.4.4" + resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26" + integrity sha512-0ckx7ZHRPqb0oUm8zNr+90mtf9DQB60H1wMCjBtfi62Kl3a7JbHob6gA2bC+xRvZoOL+1hzUK8jeuEIQE8svEQ== + magic-string@^0.25.2: version "0.25.9" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c" @@ -11595,6 +11798,11 @@ mimic-response@^3.1.0: resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== +min-indent@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" + integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== + mini-html-webpack-plugin@^0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/mini-html-webpack-plugin/-/mini-html-webpack-plugin-0.2.3.tgz#2dfbdc3f35f6ae03864a608808381f8137311ea0" @@ -13416,6 +13624,15 @@ pretty-format@^26.6.2: ansi-styles "^4.0.0" react-is "^17.0.1" +pretty-format@^27.0.2: + version "27.5.1" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" + integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== + dependencies: + ansi-regex "^5.0.1" + ansi-styles "^5.0.0" + react-is "^17.0.1" + pretty-format@^28.1.1: version "28.1.1" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-28.1.1.tgz#f731530394e0f7fcd95aba6b43c50e02d86b95cb" @@ -13426,6 +13643,15 @@ pretty-format@^28.1.1: ansi-styles "^5.0.0" react-is "^18.0.0" +pretty-format@^29.0.0, pretty-format@^29.2.0: + version "29.2.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.2.0.tgz#1d4ea56fb46079b44efd9ed59c14f70f2950a61b" + integrity sha512-QCSUFdwOi924g24czhOH5eTkXxUCqlLGZBRCySlwDYHIXRJkdGyjJc9nZaqhlFBZws8dq5Dvk0lCilsmlfsPxw== + dependencies: + "@jest/schemas" "^29.0.0" + ansi-styles "^5.0.0" + react-is "^18.0.0" + pretty-format@^3.5.1: version "3.8.0" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-3.8.0.tgz#bfbed56d5e9a776645f4b1ff7aa1a3ac4fa3c385" @@ -14388,6 +14614,14 @@ redent@^2.0.0: indent-string "^3.0.0" strip-indent "^2.0.0" +redent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" + integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg== + dependencies: + indent-string "^4.0.0" + strip-indent "^3.0.0" + redeyed@~2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/redeyed/-/redeyed-2.1.1.tgz#8984b5815d99cb220469c99eeeffe38913e6cc0b" @@ -16018,6 +16252,13 @@ strip-indent@^2.0.0: resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68" integrity sha512-RsSNPLpq6YUL7QYy44RnPVTn/lcVZtb48Uof3X5JLbF4zD/Gs7ZFDv2HWol+leoQN2mT86LAzSshGfkTlSOpsA== +strip-indent@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" + integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== + dependencies: + min-indent "^1.0.0" + strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"