@openedx/paragon 21.11.1 → 21.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,7 +3,7 @@ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { va
3
3
  function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
4
4
  function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
5
5
  function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
6
- import React, { useCallback, useEffect } from 'react';
6
+ import React from 'react';
7
7
  import PropTypes from 'prop-types';
8
8
  import classNames from 'classnames';
9
9
  import { HexColorPicker } from 'react-colorful';
@@ -22,23 +22,52 @@ function ColorPicker(_ref) {
22
22
  } = _ref;
23
23
  const [isOpen, open, close] = useToggle(false);
24
24
  const [target, setTarget] = React.useState(null);
25
- const [hexValid, setHexValid] = React.useState(true);
26
- const validateHex = useCallback(input => {
25
+ const colorIsValid = colorToValidate => {
27
26
  const hexRegex = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/;
28
- if (input.length > 1 && !input.startsWith('#')) {
29
- setColor(`#${input}`);
30
- } else {
31
- setColor(input);
27
+ return hexRegex.test(colorToValidate);
28
+ };
29
+ const formatHexColorString = colorString => {
30
+ if (!colorString.startsWith('#')) {
31
+ return `#${colorString}`.slice(0, 7);
32
32
  }
33
- if (input === '' || hexRegex.test(input) === true) {
33
+ return colorString.slice(0, 7);
34
+ };
35
+ const [hexValid, setHexValid] = React.useState(() => color === '' || colorIsValid(formatHexColorString(color)));
36
+ const [hexColorString, setHexColorString] = React.useState(() => {
37
+ if (color === '') {
38
+ return '';
39
+ }
40
+ return formatHexColorString(color);
41
+ });
42
+ const [colorToDisplay, setColorToDisplay] = React.useState(() => {
43
+ const formattedColor = formatHexColorString(color);
44
+ if (colorIsValid(formattedColor)) {
45
+ return formattedColor;
46
+ }
47
+ return '#fff';
48
+ });
49
+ const setValidatedColor = newColor => {
50
+ if (newColor === '') {
51
+ setHexValid(true);
52
+ setColor('');
53
+ setHexColorString('');
54
+ setColorToDisplay('#fff');
55
+ return;
56
+ }
57
+ const formattedColor = formatHexColorString(newColor);
58
+ if (colorIsValid(formattedColor)) {
34
59
  setHexValid(true);
35
- } else {
36
- setHexValid(false);
60
+ setColor(formattedColor);
61
+ setHexColorString(formattedColor);
62
+ setColorToDisplay(formattedColor);
63
+ return;
37
64
  }
38
- }, [setColor]);
65
+ setHexValid(false);
66
+ setHexColorString(formattedColor);
39
67
 
40
- // this is needed for when a user changes the color through the sliders
41
- useEffect(() => validateHex(color), [validateHex, color]);
68
+ // ensure the picker value stays in sync with the textbox
69
+ setColor(formattedColor);
70
+ };
42
71
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("span", {
43
72
  className: "d-flex"
44
73
  }, /*#__PURE__*/React.createElement(OverlayTrigger, {
@@ -66,8 +95,8 @@ function ColorPicker(_ref) {
66
95
  textAlign: 'start'
67
96
  }
68
97
  }, /*#__PURE__*/React.createElement(HexColorPicker, {
69
- color: color || '',
70
- onChange: setColor
98
+ color: colorToDisplay,
99
+ onChange: setValidatedColor
71
100
  }), /*#__PURE__*/React.createElement(Form.Group, {
72
101
  className: "pgn__hex-form",
73
102
  size: "sm"
@@ -76,9 +105,10 @@ function ColorPicker(_ref) {
76
105
  }, "Hex"), /*#__PURE__*/React.createElement(Form.Control, {
77
106
  className: "pgn__hex-field",
78
107
  isInvalid: !hexValid,
79
- value: color,
80
- onChange: e => validateHex(e.target.value),
81
- "data-testid": "hex-input"
108
+ value: hexColorString,
109
+ onChange: e => setValidatedColor(e.target.value),
110
+ "data-testid": "hex-input",
111
+ spellCheck: "false"
82
112
  })), !hexValid && /*#__PURE__*/React.createElement(Form.Control.Feedback, {
83
113
  className: "pgn__color-error",
84
114
  type: "invalid"
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["React","useCallback","useEffect","PropTypes","classNames","HexColorPicker","Button","Form","ModalPopup","OverlayTrigger","Tooltip","useToggle","ColorPicker","_ref","color","setColor","className","size","isOpen","open","close","target","setTarget","useState","hexValid","setHexValid","validateHex","input","hexRegex","length","startsWith","test","createElement","Fragment","placement","overlay","id","ref","style","_objectSpread","background","onClick","positionRef","onClose","textAlign","onChange","Group","Label","Control","isInvalid","value","e","Feedback","type","defaultProps","undefined","propTypes","string","func","isRequired","oneOf"],"sources":["../../src/ColorPicker/index.jsx"],"sourcesContent":["import React, { useCallback, useEffect } from 'react';\nimport PropTypes from 'prop-types';\nimport classNames from 'classnames';\nimport { HexColorPicker } from 'react-colorful';\n\nimport Button from '../Button';\nimport Form from '../Form';\nimport ModalPopup from '../Modal/ModalPopup';\nimport { OverlayTrigger } from '../Overlay';\nimport Tooltip from '../Tooltip';\nimport useToggle from '../hooks/useToggle';\n\nfunction ColorPicker({\n color, setColor, className, size,\n}) {\n const [isOpen, open, close] = useToggle(false);\n const [target, setTarget] = React.useState(null);\n const [hexValid, setHexValid] = React.useState(true);\n\n const validateHex = useCallback((input) => {\n const hexRegex = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/;\n if (input.length > 1 && !input.startsWith('#')) {\n setColor(`#${input}`);\n } else {\n setColor(input);\n }\n if (input === '' || hexRegex.test(input) === true) {\n setHexValid(true);\n } else {\n setHexValid(false);\n }\n }, [setColor]);\n\n // this is needed for when a user changes the color through the sliders\n useEffect(() => validateHex(color), [validateHex, color]);\n\n return (\n <>\n <span className=\"d-flex\">\n <OverlayTrigger\n placement=\"top\"\n overlay={<Tooltip id=\"color-picker-tooltip\">Color picker</Tooltip>}\n >\n <Button\n ref={setTarget}\n className={classNames(\n className,\n 'pgn__color-picker',\n `pgn__color-picker-${size}`,\n )}\n style={{\n ...(color && hexValid ? { background: `${color}` } : {}),\n }}\n onClick={open}\n />\n </OverlayTrigger>\n </span>\n <ModalPopup\n positionRef={target}\n isOpen={isOpen}\n style={{ background: 'black' }}\n onClose={close}\n >\n <div\n className=\"pgn__color-modal rounded shadow\"\n style={{ textAlign: 'start' }}\n >\n <HexColorPicker color={color || ''} onChange={setColor} />\n <Form.Group className=\"pgn__hex-form\" size=\"sm\">\n <div>\n <Form.Label className=\"pgn__hex-label\">Hex</Form.Label>\n <Form.Control\n className=\"pgn__hex-field\"\n isInvalid={!hexValid}\n value={color}\n onChange={(e) => validateHex(e.target.value)}\n data-testid=\"hex-input\"\n />\n </div>\n {!hexValid && (\n <Form.Control.Feedback\n className=\"pgn__color-error\"\n type=\"invalid\"\n >\n Colors must be in hexadecimal format.\n </Form.Control.Feedback>\n )}\n </Form.Group>\n </div>\n </ModalPopup>\n </>\n );\n}\n\nColorPicker.defaultProps = {\n color: '',\n className: undefined,\n size: 'md',\n};\n\nColorPicker.propTypes = {\n /** A default hex code to preset the picker to display. */\n color: PropTypes.string,\n /** Passing setState function allows parent to alter the color. */\n setColor: PropTypes.func.isRequired,\n /** A class name to append to the base element. */\n className: PropTypes.string,\n /** Size of the color picker */\n size: PropTypes.oneOf(['sm', 'md']),\n};\n\nexport default ColorPicker;\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["React","PropTypes","classNames","HexColorPicker","Button","Form","ModalPopup","OverlayTrigger","Tooltip","useToggle","ColorPicker","_ref","color","setColor","className","size","isOpen","open","close","target","setTarget","useState","colorIsValid","colorToValidate","hexRegex","test","formatHexColorString","colorString","startsWith","slice","hexValid","setHexValid","hexColorString","setHexColorString","colorToDisplay","setColorToDisplay","formattedColor","setValidatedColor","newColor","createElement","Fragment","placement","overlay","id","ref","style","_objectSpread","background","onClick","positionRef","onClose","textAlign","onChange","Group","Label","Control","isInvalid","value","e","spellCheck","Feedback","type","defaultProps","undefined","propTypes","string","func","isRequired","oneOf"],"sources":["../../src/ColorPicker/index.jsx"],"sourcesContent":["import React from 'react';\nimport PropTypes from 'prop-types';\nimport classNames from 'classnames';\nimport { HexColorPicker } from 'react-colorful';\n\nimport Button from '../Button';\nimport Form from '../Form';\nimport ModalPopup from '../Modal/ModalPopup';\nimport { OverlayTrigger } from '../Overlay';\nimport Tooltip from '../Tooltip';\nimport useToggle from '../hooks/useToggle';\n\nfunction ColorPicker({\n color, setColor, className, size,\n}) {\n const [isOpen, open, close] = useToggle(false);\n const [target, setTarget] = React.useState(null);\n\n const colorIsValid = (colorToValidate) => {\n const hexRegex = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/;\n return hexRegex.test(colorToValidate);\n };\n\n const formatHexColorString = (colorString) => {\n if (!colorString.startsWith('#')) {\n return `#${colorString}`.slice(0, 7);\n }\n\n return colorString.slice(0, 7);\n };\n\n const [hexValid, setHexValid] = React.useState(() => (color === '' || colorIsValid(formatHexColorString(color))));\n\n const [hexColorString, setHexColorString] = React.useState(() => {\n if (color === '') {\n return '';\n }\n\n return formatHexColorString(color);\n });\n const [colorToDisplay, setColorToDisplay] = React.useState(() => {\n const formattedColor = formatHexColorString(color);\n if (colorIsValid(formattedColor)) {\n return formattedColor;\n }\n\n return '#fff';\n });\n\n const setValidatedColor = (newColor) => {\n if (newColor === '') {\n setHexValid(true);\n setColor('');\n setHexColorString('');\n setColorToDisplay('#fff');\n return;\n }\n\n const formattedColor = formatHexColorString(newColor);\n\n if (colorIsValid(formattedColor)) {\n setHexValid(true);\n setColor(formattedColor);\n setHexColorString(formattedColor);\n setColorToDisplay(formattedColor);\n return;\n }\n\n setHexValid(false);\n setHexColorString(formattedColor);\n\n // ensure the picker value stays in sync with the textbox\n setColor(formattedColor);\n };\n\n return (\n <>\n <span className=\"d-flex\">\n <OverlayTrigger\n placement=\"top\"\n overlay={<Tooltip id=\"color-picker-tooltip\">Color picker</Tooltip>}\n >\n <Button\n ref={setTarget}\n className={classNames(\n className,\n 'pgn__color-picker',\n `pgn__color-picker-${size}`,\n )}\n style={{\n ...(color && hexValid ? { background: `${color}` } : {}),\n }}\n onClick={open}\n />\n </OverlayTrigger>\n </span>\n <ModalPopup\n positionRef={target}\n isOpen={isOpen}\n style={{ background: 'black' }}\n onClose={close}\n >\n <div\n className=\"pgn__color-modal rounded shadow\"\n style={{ textAlign: 'start' }}\n >\n <HexColorPicker color={colorToDisplay} onChange={setValidatedColor} />\n <Form.Group className=\"pgn__hex-form\" size=\"sm\">\n <div>\n <Form.Label className=\"pgn__hex-label\">Hex</Form.Label>\n <Form.Control\n className=\"pgn__hex-field\"\n isInvalid={!hexValid}\n value={hexColorString}\n onChange={(e) => setValidatedColor(e.target.value)}\n data-testid=\"hex-input\"\n spellCheck=\"false\"\n />\n </div>\n {!hexValid && (\n <Form.Control.Feedback\n className=\"pgn__color-error\"\n type=\"invalid\"\n >\n Colors must be in hexadecimal format.\n </Form.Control.Feedback>\n )}\n </Form.Group>\n </div>\n </ModalPopup>\n </>\n );\n}\n\nColorPicker.defaultProps = {\n color: '',\n className: undefined,\n size: 'md',\n};\n\nColorPicker.propTypes = {\n /** A default hex code to preset the picker to display. */\n color: PropTypes.string,\n /** Passing setState function allows parent to alter the color. */\n setColor: PropTypes.func.isRequired,\n /** A class name to append to the base element. */\n className: PropTypes.string,\n /** Size of the color picker */\n size: PropTypes.oneOf(['sm', 'md']),\n};\n\nexport default ColorPicker;\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openedx/paragon",
3
- "version": "21.11.1",
3
+ "version": "21.11.2",
4
4
  "description": "Accessible, responsive UI component library based on Bootstrap.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",
@@ -100,11 +100,11 @@
100
100
  "@formatjs/cli": "^5.0.2",
101
101
  "@semantic-release/changelog": "^6.0.1",
102
102
  "@semantic-release/git": "^10.0.1",
103
- "@testing-library/jest-dom": "^5.16.3",
103
+ "@testing-library/jest-dom": "^6.1.4",
104
104
  "@testing-library/react": "^12.1.4",
105
105
  "@testing-library/react-hooks": "^8.0.1",
106
106
  "@testing-library/user-event": "^13.5.0",
107
- "@types/jest": "^27.5.0",
107
+ "@types/jest": "^29.5.10",
108
108
  "@types/react": "17.0.0",
109
109
  "@types/react-dom": "17.0.11",
110
110
  "@types/react-test-renderer": "^18.0.0",
@@ -29,13 +29,35 @@ describe('picker works as expected', () => {
29
29
  const color = 'wassap';
30
30
  const setColor = jest.fn();
31
31
  it('validates hex color', async () => {
32
- const { rerender } = render(<ColorPicker color={color} setColor={setColor} />);
32
+ render(<ColorPicker color={color} setColor={setColor} />);
33
+
33
34
  await act(async () => {
34
35
  await userEvent.click(screen.getByRole('button'));
35
36
  });
37
+ expect(screen.queryByTestId('hex-input').value).toEqual('#wassap');
36
38
  expect(screen.queryByText('Colors must be in hexadecimal format.')).toBeInTheDocument();
37
39
 
38
- rerender(<ColorPicker color="#32116c" setColor={setColor} />);
40
+ await act(async () => {
41
+ await userEvent.clear(screen.getByTestId('hex-input'));
42
+ await userEvent.paste(screen.getByTestId('hex-input'), '32116c');
43
+ });
44
+ expect(screen.queryByTestId('hex-input').value).toEqual('#32116c');
45
+ expect(screen.queryByText('Colors must be in hexadecimal format.')).not.toBeInTheDocument();
46
+
47
+ await act(async () => {
48
+ await userEvent.clear(screen.getByTestId('hex-input'));
49
+ await userEvent.paste(screen.getByTestId('hex-input'), 'yuk');
50
+ });
51
+
52
+ expect(screen.queryByTestId('hex-input').value).toEqual('#yuk');
53
+ expect(screen.queryByText('Colors must be in hexadecimal format.')).toBeInTheDocument();
54
+
55
+ await act(async () => {
56
+ await userEvent.clear(screen.getByTestId('hex-input'));
57
+ await userEvent.paste(screen.getByTestId('hex-input'), '#fad');
58
+ });
59
+
60
+ expect(screen.queryByTestId('hex-input').value).toEqual('#fad');
39
61
  expect(screen.queryByText('Colors must be in hexadecimal format.')).not.toBeInTheDocument();
40
62
  });
41
63
  });
@@ -1,4 +1,4 @@
1
- import React, { useCallback, useEffect } from 'react';
1
+ import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import classNames from 'classnames';
4
4
  import { HexColorPicker } from 'react-colorful';
@@ -15,24 +15,63 @@ function ColorPicker({
15
15
  }) {
16
16
  const [isOpen, open, close] = useToggle(false);
17
17
  const [target, setTarget] = React.useState(null);
18
- const [hexValid, setHexValid] = React.useState(true);
19
18
 
20
- const validateHex = useCallback((input) => {
19
+ const colorIsValid = (colorToValidate) => {
21
20
  const hexRegex = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/;
22
- if (input.length > 1 && !input.startsWith('#')) {
23
- setColor(`#${input}`);
24
- } else {
25
- setColor(input);
21
+ return hexRegex.test(colorToValidate);
22
+ };
23
+
24
+ const formatHexColorString = (colorString) => {
25
+ if (!colorString.startsWith('#')) {
26
+ return `#${colorString}`.slice(0, 7);
27
+ }
28
+
29
+ return colorString.slice(0, 7);
30
+ };
31
+
32
+ const [hexValid, setHexValid] = React.useState(() => (color === '' || colorIsValid(formatHexColorString(color))));
33
+
34
+ const [hexColorString, setHexColorString] = React.useState(() => {
35
+ if (color === '') {
36
+ return '';
37
+ }
38
+
39
+ return formatHexColorString(color);
40
+ });
41
+ const [colorToDisplay, setColorToDisplay] = React.useState(() => {
42
+ const formattedColor = formatHexColorString(color);
43
+ if (colorIsValid(formattedColor)) {
44
+ return formattedColor;
26
45
  }
27
- if (input === '' || hexRegex.test(input) === true) {
46
+
47
+ return '#fff';
48
+ });
49
+
50
+ const setValidatedColor = (newColor) => {
51
+ if (newColor === '') {
28
52
  setHexValid(true);
29
- } else {
30
- setHexValid(false);
53
+ setColor('');
54
+ setHexColorString('');
55
+ setColorToDisplay('#fff');
56
+ return;
31
57
  }
32
- }, [setColor]);
33
58
 
34
- // this is needed for when a user changes the color through the sliders
35
- useEffect(() => validateHex(color), [validateHex, color]);
59
+ const formattedColor = formatHexColorString(newColor);
60
+
61
+ if (colorIsValid(formattedColor)) {
62
+ setHexValid(true);
63
+ setColor(formattedColor);
64
+ setHexColorString(formattedColor);
65
+ setColorToDisplay(formattedColor);
66
+ return;
67
+ }
68
+
69
+ setHexValid(false);
70
+ setHexColorString(formattedColor);
71
+
72
+ // ensure the picker value stays in sync with the textbox
73
+ setColor(formattedColor);
74
+ };
36
75
 
37
76
  return (
38
77
  <>
@@ -65,16 +104,17 @@ function ColorPicker({
65
104
  className="pgn__color-modal rounded shadow"
66
105
  style={{ textAlign: 'start' }}
67
106
  >
68
- <HexColorPicker color={color || ''} onChange={setColor} />
107
+ <HexColorPicker color={colorToDisplay} onChange={setValidatedColor} />
69
108
  <Form.Group className="pgn__hex-form" size="sm">
70
109
  <div>
71
110
  <Form.Label className="pgn__hex-label">Hex</Form.Label>
72
111
  <Form.Control
73
112
  className="pgn__hex-field"
74
113
  isInvalid={!hexValid}
75
- value={color}
76
- onChange={(e) => validateHex(e.target.value)}
114
+ value={hexColorString}
115
+ onChange={(e) => setValidatedColor(e.target.value)}
77
116
  data-testid="hex-input"
117
+ spellCheck="false"
78
118
  />
79
119
  </div>
80
120
  {!hexValid && (
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import { render, screen } from '@testing-library/react';
3
- import '@testing-library/jest-dom/extend-expect';
3
+ import '@testing-library/jest-dom';
4
4
  import userEvent from '@testing-library/user-event';
5
5
  import SelectableBox from '..';
6
6