@openedx/paragon 23.0.0-alpha.1 → 23.0.0-alpha.3
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.
- package/README.md +31 -22
- package/dist/Button/index.d.ts +35 -0
- package/dist/Button/index.js +37 -15
- package/dist/Button/index.js.map +1 -1
- package/dist/Button/index.scss +0 -2
- package/dist/Chip/ChipIcon.d.ts +13 -8
- package/dist/Chip/ChipIcon.js +0 -2
- package/dist/Chip/ChipIcon.js.map +1 -1
- package/dist/Chip/constants.d.ts +4 -0
- package/dist/Chip/constants.js +3 -2
- package/dist/Chip/constants.js.map +1 -0
- package/dist/Chip/index.d.ts +4 -3
- package/dist/Chip/index.js +2 -4
- package/dist/Chip/index.js.map +1 -1
- package/dist/Chip/index.scss +6 -5
- package/dist/Chip/mixins.scss +4 -4
- package/dist/ChipCarousel/index.js +0 -2
- package/dist/ChipCarousel/index.js.map +1 -1
- package/dist/Collapsible/index.scss +3 -3
- package/dist/Hyperlink/index.d.ts +24 -0
- package/dist/Hyperlink/index.js +20 -32
- package/dist/Hyperlink/index.js.map +1 -1
- package/dist/Icon/index.d.ts +4 -2
- package/dist/Icon/index.js +1 -1
- package/dist/Icon/index.js.map +1 -1
- package/dist/IconButton/index.d.ts +342 -0
- package/dist/IconButton/index.js +18 -26
- package/dist/IconButton/index.js.map +1 -1
- package/dist/Modal/ModalPopup.js +7 -1
- package/dist/Modal/ModalPopup.js.map +1 -1
- package/dist/Modal/_ModalDialog.scss +4 -0
- package/dist/Overlay/index.d.ts +128 -0
- package/dist/Overlay/index.js +8 -2
- package/dist/Overlay/index.js.map +1 -1
- package/dist/Stepper/index.scss +3 -2
- package/dist/Tabs/index.scss +9 -6
- package/dist/Tooltip/index.d.ts +7 -0
- package/dist/Tooltip/index.js.map +1 -1
- package/dist/core.css +22 -30
- package/dist/core.css.map +1 -1
- package/dist/core.min.css +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.js +7 -7
- package/dist/light.css +11 -11
- package/dist/light.css.map +1 -1
- package/dist/light.min.css +1 -1
- package/dist/setupTest.d.ts +2 -0
- package/dist/setupTest.js.map +1 -0
- package/dist/utils/types/bootstrap.d.ts +39 -0
- package/dist/utils/types/bootstrap.js +2 -0
- package/dist/utils/types/bootstrap.js.map +1 -0
- package/lib/build-tokens.js +18 -4
- package/package.json +6 -5
- package/src/Button/{Button.test.jsx → Button.test.tsx} +14 -2
- package/src/Button/__snapshots__/{Button.test.jsx.snap → Button.test.tsx.snap} +19 -2
- package/src/Button/index.scss +0 -2
- package/src/Button/{index.jsx → index.tsx} +58 -16
- package/src/Chip/{Chip.test.jsx → Chip.test.tsx} +5 -7
- package/src/Chip/ChipIcon.tsx +8 -8
- package/src/Chip/{constants.js → constants.ts} +1 -1
- package/src/Chip/index.scss +6 -5
- package/src/Chip/index.tsx +6 -8
- package/src/Chip/mixins.scss +4 -4
- package/src/ChipCarousel/index.tsx +0 -2
- package/src/Collapsible/index.scss +3 -3
- package/src/Hyperlink/{Hyperlink.test.jsx → Hyperlink.test.tsx} +21 -10
- package/src/Hyperlink/{index.jsx → index.tsx} +41 -37
- package/src/Icon/index.d.ts +4 -2
- package/src/Icon/index.jsx +1 -1
- package/src/IconButton/{IconButton.test.jsx → IconButton.test.tsx} +24 -3
- package/src/IconButton/__snapshots__/IconButton.test.tsx.snap +90 -0
- package/src/IconButton/{index.jsx → index.tsx} +66 -26
- package/src/Modal/ModalPopup.jsx +9 -1
- package/src/Modal/_ModalDialog.scss +4 -0
- package/src/Modal/tests/ModalPopupNoMock.test.jsx +29 -0
- package/src/Overlay/{index.jsx → index.tsx} +13 -8
- package/src/Stepper/index.scss +3 -2
- package/src/Tabs/index.scss +9 -6
- package/src/Tooltip/{index.jsx → index.tsx} +9 -3
- package/src/index.d.ts +5 -5
- package/src/index.js +7 -7
- package/src/{setupTest.js → setupTest.ts} +1 -0
- package/src/utils/types/bootstrap.test.tsx +86 -0
- package/src/utils/types/bootstrap.ts +43 -0
- package/styles/css/core/variables.css +11 -22
- package/styles/css/themes/light/variables.css +11 -11
- package/styles/scss/core/_variables.scss +4 -5
- package/styles/scss/core/core.scss +1 -1
- package/tokens/README.md +1 -2
- package/tokens/src/core/alias/size.json +3 -3
- package/tokens/src/core/components/Breadcrumb.json +0 -14
- package/tokens/src/core/components/Card.json +6 -1
- package/tokens/src/core/components/Chip.json +4 -6
- package/tokens/src/core/components/ColorPicker.json +2 -2
- package/tokens/src/core/components/DataTable.json +1 -1
- package/tokens/src/core/components/Form/size.json +3 -7
- package/tokens/src/core/components/Nav.json +0 -3
- package/tokens/src/core/components/Pagination.json +0 -4
- package/tokens/src/core/components/ProductTour.json +0 -5
- package/tokens/src/core/global/display.json +2 -1
- package/tokens/src/core/global/spacing.json +7 -5
- package/tokens/src/themes/light/alias/color.json +2 -2
- package/tokens/src/themes/light/components/Alert.json +0 -9
- package/tokens/src/themes/light/components/Annotation.json +11 -11
- package/tokens/src/themes/light/components/Avatar.json +1 -1
- package/tokens/src/themes/light/components/Breadcrumb.json +0 -1
- package/tokens/src/themes/light/components/Card.json +2 -6
- package/tokens/src/themes/light/components/DataTable.json +1 -1
- package/tokens/src/themes/light/components/Form/color.json +4 -4
- package/tokens/src/themes/light/components/Form/elevation.json +1 -1
- package/tokens/src/themes/light/components/Form/other.json +3 -3
- package/tokens/src/themes/light/components/general/input.json +1 -1
- package/tokens/src/themes/light/components/general/link.json +1 -1
- package/tokens/src/themes/light/components/general/list.json +1 -1
- package/tokens/src/themes/light/components/general/text.json +7 -1
- package/tokens/src/themes/light/global/color.json +2 -2
- package/tokens/style-dictionary.js +6 -0
- package/src/IconButton/__snapshots__/IconButton.test.jsx.snap +0 -20
- /package/src/Button/{ButtonGroup.test.jsx → ButtonGroup.test.tsx} +0 -0
- /package/src/Button/{ButtonToolbar.test.jsx → ButtonToolbar.test.tsx} +0 -0
- /package/src/Button/__snapshots__/{ButtonGroup.test.jsx.snap → ButtonGroup.test.tsx.snap} +0 -0
- /package/src/Button/__snapshots__/{ButtonToolbar.test.jsx.snap → ButtonToolbar.test.tsx.snap} +0 -0
- /package/src/Chip/__snapshots__/{Chip.test.jsx.snap → Chip.test.tsx.snap} +0 -0
- /package/src/Tooltip/{Tooltip.test.jsx → Tooltip.test.tsx} +0 -0
|
@@ -1,29 +1,45 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import classNames from 'classnames';
|
|
4
|
-
import isRequiredIf from 'react-proptype-conditional-require';
|
|
5
4
|
import { Launch } from '../../icons';
|
|
6
5
|
import Icon from '../Icon';
|
|
7
6
|
|
|
8
|
-
import withDeprecatedProps, { DeprTypes } from '../withDeprecatedProps';
|
|
9
|
-
|
|
10
7
|
export const HYPER_LINK_EXTERNAL_LINK_ALT_TEXT = 'in a new tab';
|
|
11
8
|
export const HYPER_LINK_EXTERNAL_LINK_TITLE = 'Opens in a new tab';
|
|
12
9
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
10
|
+
interface Props extends Omit<React.ComponentPropsWithRef<'a'>, 'href' | 'target'> {
|
|
11
|
+
/** specifies the URL */
|
|
12
|
+
destination: string;
|
|
13
|
+
/** Content of the hyperlink */
|
|
14
|
+
children: React.ReactNode;
|
|
15
|
+
/** Custom class names for the hyperlink */
|
|
16
|
+
className?: string;
|
|
17
|
+
/** Alt text for the icon indicating that this link opens in a new tab, if target="_blank". e.g. _("in a new tab") */
|
|
18
|
+
externalLinkAlternativeText?: string;
|
|
19
|
+
/** Tooltip text for the "opens in new tab" icon, if target="_blank". e.g. _("Opens in a new tab"). */
|
|
20
|
+
externalLinkTitle?: string;
|
|
21
|
+
/** type of hyperlink */
|
|
22
|
+
variant?: 'default' | 'muted' | 'brand';
|
|
23
|
+
/** Display the link with an underline. By default, it is only underlined on hover. */
|
|
24
|
+
isInline?: boolean;
|
|
25
|
+
/** specify if we need to show launch Icon. By default, it will be visible. */
|
|
26
|
+
showLaunchIcon?: boolean;
|
|
27
|
+
target?: '_blank' | '_self';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const Hyperlink = React.forwardRef<HTMLAnchorElement, Props>(({
|
|
31
|
+
className,
|
|
32
|
+
destination,
|
|
33
|
+
children,
|
|
34
|
+
target,
|
|
35
|
+
onClick,
|
|
36
|
+
externalLinkAlternativeText,
|
|
37
|
+
externalLinkTitle,
|
|
38
|
+
variant,
|
|
39
|
+
isInline,
|
|
40
|
+
showLaunchIcon,
|
|
41
|
+
...attrs
|
|
42
|
+
}, ref) => {
|
|
27
43
|
let externalLinkIcon;
|
|
28
44
|
|
|
29
45
|
if (target === '_blank') {
|
|
@@ -105,32 +121,20 @@ Hyperlink.propTypes = {
|
|
|
105
121
|
* loaded into the same browsing context as the current one.
|
|
106
122
|
* If the target is `_blank` (opening a new window) `rel='noopener'` will be added to the anchor tag to prevent
|
|
107
123
|
* any potential [reverse tabnabbing attack](https://www.owasp.org/index.php/Reverse_Tabnabbing).
|
|
108
|
-
|
|
109
|
-
target: PropTypes.
|
|
124
|
+
*/
|
|
125
|
+
target: PropTypes.oneOf(['_blank', '_self']),
|
|
110
126
|
/** specifies the callback function when the link is clicked */
|
|
111
127
|
onClick: PropTypes.func,
|
|
112
|
-
/**
|
|
113
|
-
externalLinkAlternativeText:
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
),
|
|
117
|
-
/** specifies the title for links with a `_blank` target (which loads the URL in a new browsing context). */
|
|
118
|
-
externalLinkTitle: isRequiredIf(
|
|
119
|
-
PropTypes.string,
|
|
120
|
-
props => props.target === '_blank',
|
|
121
|
-
),
|
|
128
|
+
/** Alt text for the icon indicating that this link opens in a new tab, if target="_blank". e.g. _("in a new tab") */
|
|
129
|
+
externalLinkAlternativeText: PropTypes.string,
|
|
130
|
+
/** Tooltip text for the "opens in new tab" icon, if target="_blank". e.g. _("Opens in a new tab"). */
|
|
131
|
+
externalLinkTitle: PropTypes.string,
|
|
122
132
|
/** type of hyperlink */
|
|
123
133
|
variant: PropTypes.oneOf(['default', 'muted', 'brand']),
|
|
124
|
-
/**
|
|
134
|
+
/** Display the link with an underline. By default, it is only underlined on hover. */
|
|
125
135
|
isInline: PropTypes.bool,
|
|
126
136
|
/** specify if we need to show launch Icon. By default, it will be visible. */
|
|
127
137
|
showLaunchIcon: PropTypes.bool,
|
|
128
138
|
};
|
|
129
139
|
|
|
130
|
-
export default
|
|
131
|
-
/** specifies the text or element that a URL should be associated with */
|
|
132
|
-
content: {
|
|
133
|
-
deprType: DeprTypes.MOVED,
|
|
134
|
-
newName: 'children',
|
|
135
|
-
},
|
|
136
|
-
});
|
|
140
|
+
export default Hyperlink;
|
package/src/Icon/index.d.ts
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
|
|
3
3
|
export interface IconProps extends React.ComponentPropsWithoutRef<'span'> {
|
|
4
|
-
|
|
4
|
+
// Note: React.ComponentType is what we want here. React.ElementType would allow some element type strings like "div",
|
|
5
|
+
// but we only want to allow components like 'Add' (a specific icon component function/class)
|
|
6
|
+
src?: React.ComponentType;
|
|
5
7
|
svgAttrs?: {
|
|
6
8
|
'aria-label'?: string;
|
|
7
9
|
'aria-labelledby'?: string;
|
|
8
10
|
};
|
|
9
11
|
id?: string | null;
|
|
10
|
-
size?: 'xs' | 'sm' | 'md' | 'lg';
|
|
12
|
+
size?: 'xs' | 'sm' | 'md' | 'lg' | 'inline';
|
|
11
13
|
className?: string | string[];
|
|
12
14
|
hidden?: boolean;
|
|
13
15
|
screenReaderText?: React.ReactNode;
|
package/src/Icon/index.jsx
CHANGED
|
@@ -74,7 +74,7 @@ Icon.propTypes = {
|
|
|
74
74
|
* An icon component to render.
|
|
75
75
|
* Example import of a Paragon icon component: `import { Check } from '@openedx/paragon/icons';`
|
|
76
76
|
*/
|
|
77
|
-
src: PropTypes.
|
|
77
|
+
src: PropTypes.elementType,
|
|
78
78
|
/** HTML element attributes to pass through to the underlying svg element */
|
|
79
79
|
svgAttrs: PropTypes.shape({
|
|
80
80
|
'aria-label': PropTypes.string,
|
|
@@ -11,21 +11,27 @@ describe('<IconButton />', () => {
|
|
|
11
11
|
const alt = 'alternative';
|
|
12
12
|
const iconAs = Icon;
|
|
13
13
|
const src = InfoOutline;
|
|
14
|
-
const variant = 'secondary';
|
|
14
|
+
const variant = 'secondary' as const;
|
|
15
15
|
const props = {
|
|
16
16
|
alt,
|
|
17
17
|
src,
|
|
18
18
|
iconAs,
|
|
19
19
|
variant,
|
|
20
20
|
};
|
|
21
|
-
const
|
|
21
|
+
const deprecatedFontAwesomeExample = {
|
|
22
22
|
prefix: 'pgn',
|
|
23
23
|
iconName: 'InfoOutlineIcon',
|
|
24
24
|
icon: [InfoOutline],
|
|
25
25
|
};
|
|
26
26
|
it('renders with required props', () => {
|
|
27
27
|
const tree = renderer.create((
|
|
28
|
-
<IconButton
|
|
28
|
+
<IconButton iconAs={Icon} src={InfoOutline} alt={alt} />
|
|
29
|
+
)).toJSON();
|
|
30
|
+
expect(tree).toMatchSnapshot();
|
|
31
|
+
});
|
|
32
|
+
it('renders with deprecated props', () => {
|
|
33
|
+
const tree = renderer.create((
|
|
34
|
+
<IconButton icon={deprecatedFontAwesomeExample} alt={alt} />
|
|
29
35
|
)).toJSON();
|
|
30
36
|
expect(tree).toMatchSnapshot();
|
|
31
37
|
});
|
|
@@ -94,4 +100,19 @@ describe('<IconButton />', () => {
|
|
|
94
100
|
expect(spy2).toHaveBeenCalledTimes(1);
|
|
95
101
|
});
|
|
96
102
|
});
|
|
103
|
+
|
|
104
|
+
describe('<IconButton.IconButtonWithTooltip>', () => {
|
|
105
|
+
it('renders with required props', () => {
|
|
106
|
+
const tree = renderer.create((
|
|
107
|
+
<IconButton.IconButtonWithTooltip
|
|
108
|
+
iconAs={Icon}
|
|
109
|
+
src={InfoOutline}
|
|
110
|
+
alt={alt}
|
|
111
|
+
tooltipContent="Hello"
|
|
112
|
+
tooltipPlacement="left-end"
|
|
113
|
+
/>
|
|
114
|
+
)).toJSON();
|
|
115
|
+
expect(tree).toMatchSnapshot();
|
|
116
|
+
});
|
|
117
|
+
});
|
|
97
118
|
});
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`<IconButton /> <IconButton.IconButtonWithTooltip> renders with required props 1`] = `
|
|
4
|
+
<button
|
|
5
|
+
aria-label="alternative"
|
|
6
|
+
className="btn-icon btn-icon-primary btn-icon-md"
|
|
7
|
+
onBlur={[Function]}
|
|
8
|
+
onClick={[Function]}
|
|
9
|
+
onFocus={[Function]}
|
|
10
|
+
onMouseOut={[Function]}
|
|
11
|
+
onMouseOver={[Function]}
|
|
12
|
+
type="button"
|
|
13
|
+
>
|
|
14
|
+
<span
|
|
15
|
+
className="btn-icon__icon-container"
|
|
16
|
+
>
|
|
17
|
+
<span
|
|
18
|
+
className="pgn__icon btn-icon__icon"
|
|
19
|
+
>
|
|
20
|
+
<svg
|
|
21
|
+
aria-hidden={true}
|
|
22
|
+
fill="none"
|
|
23
|
+
focusable={false}
|
|
24
|
+
height={24}
|
|
25
|
+
role="img"
|
|
26
|
+
viewBox="0 0 24 24"
|
|
27
|
+
width={24}
|
|
28
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
29
|
+
>
|
|
30
|
+
<path
|
|
31
|
+
d="M11 7h2v2h-2V7Zm0 4h2v6h-2v-6Zm1-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2Zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8Z"
|
|
32
|
+
fill="currentColor"
|
|
33
|
+
/>
|
|
34
|
+
</svg>
|
|
35
|
+
</span>
|
|
36
|
+
</span>
|
|
37
|
+
</button>
|
|
38
|
+
`;
|
|
39
|
+
|
|
40
|
+
exports[`<IconButton /> renders with deprecated props 1`] = `
|
|
41
|
+
<button
|
|
42
|
+
aria-label="alternative"
|
|
43
|
+
className="btn-icon btn-icon-primary btn-icon-md"
|
|
44
|
+
onClick={[Function]}
|
|
45
|
+
type="button"
|
|
46
|
+
>
|
|
47
|
+
<span
|
|
48
|
+
className="btn-icon__icon-container"
|
|
49
|
+
>
|
|
50
|
+
<span
|
|
51
|
+
aria-hidden={true}
|
|
52
|
+
className="btn-icon__icon"
|
|
53
|
+
id="Icon1"
|
|
54
|
+
/>
|
|
55
|
+
</span>
|
|
56
|
+
</button>
|
|
57
|
+
`;
|
|
58
|
+
|
|
59
|
+
exports[`<IconButton /> renders with required props 1`] = `
|
|
60
|
+
<button
|
|
61
|
+
aria-label="alternative"
|
|
62
|
+
className="btn-icon btn-icon-primary btn-icon-md"
|
|
63
|
+
onClick={[Function]}
|
|
64
|
+
type="button"
|
|
65
|
+
>
|
|
66
|
+
<span
|
|
67
|
+
className="btn-icon__icon-container"
|
|
68
|
+
>
|
|
69
|
+
<span
|
|
70
|
+
className="pgn__icon btn-icon__icon"
|
|
71
|
+
>
|
|
72
|
+
<svg
|
|
73
|
+
aria-hidden={true}
|
|
74
|
+
fill="none"
|
|
75
|
+
focusable={false}
|
|
76
|
+
height={24}
|
|
77
|
+
role="img"
|
|
78
|
+
viewBox="0 0 24 24"
|
|
79
|
+
width={24}
|
|
80
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
81
|
+
>
|
|
82
|
+
<path
|
|
83
|
+
d="M11 7h2v2h-2V7Zm0 4h2v6h-2v-6Zm1-9C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2Zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8Z"
|
|
84
|
+
fill="currentColor"
|
|
85
|
+
/>
|
|
86
|
+
</svg>
|
|
87
|
+
</span>
|
|
88
|
+
</span>
|
|
89
|
+
</button>
|
|
90
|
+
`;
|
|
@@ -1,12 +1,44 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import classNames from 'classnames';
|
|
4
|
-
|
|
4
|
+
import { type Placement } from 'react-bootstrap/Overlay';
|
|
5
5
|
import Icon from '../Icon';
|
|
6
6
|
import { OverlayTrigger } from '../Overlay';
|
|
7
7
|
import Tooltip from '../Tooltip';
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
interface Props extends React.HTMLAttributes<HTMLButtonElement> {
|
|
10
|
+
iconAs?: React.ComponentType<any>,
|
|
11
|
+
/** Additional CSS class[es] to apply to this button */
|
|
12
|
+
className?: string;
|
|
13
|
+
/** Alt text for your icon. For best practice, avoid using alt text to describe
|
|
14
|
+
* the image in the `IconButton`. Instead, we recommend describing the function
|
|
15
|
+
* of the button. */
|
|
16
|
+
alt: string;
|
|
17
|
+
/** Changes icon styles for dark background */
|
|
18
|
+
invertColors?: boolean;
|
|
19
|
+
/** An icon component to render. Example import of a Paragon icon component:
|
|
20
|
+
* `import { Check } from '@openedx/paragon/icons';`
|
|
21
|
+
* */
|
|
22
|
+
// Note: React.ComponentType is what we want here. React.ElementType would allow some element type strings like "div",
|
|
23
|
+
// but we only want to allow components like 'Add' (a specific icon component function/class)
|
|
24
|
+
src?: React.ComponentType;
|
|
25
|
+
/** Extra class names that will be added to the icon */
|
|
26
|
+
iconClassNames?: string;
|
|
27
|
+
/** Click handler for the button */
|
|
28
|
+
onClick?: React.MouseEventHandler<HTMLButtonElement>;
|
|
29
|
+
/** whether to show the `IconButton` in an active state, whose styling is distinct from default state */
|
|
30
|
+
isActive?: boolean;
|
|
31
|
+
/** @deprecated Using FontAwesome icons is deprecated. Instead, pass iconAs={Icon} src={...} */
|
|
32
|
+
icon?: { prefix?: string; iconName?: string, icon?: any[] },
|
|
33
|
+
/** Type of button (uses Bootstrap options) */
|
|
34
|
+
variant?: 'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'light' | 'dark' | 'black' | 'brand';
|
|
35
|
+
/** size of button to render */
|
|
36
|
+
size?: 'sm' | 'md' | 'inline';
|
|
37
|
+
/** no children */
|
|
38
|
+
children?: never;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const IconButton = React.forwardRef<HTMLButtonElement, Props>(({
|
|
10
42
|
className,
|
|
11
43
|
alt,
|
|
12
44
|
invertColors,
|
|
@@ -18,6 +50,7 @@ const IconButton = React.forwardRef(({
|
|
|
18
50
|
variant,
|
|
19
51
|
iconAs,
|
|
20
52
|
isActive,
|
|
53
|
+
children, // unused, just here because we don't want it to be part of 'attrs'
|
|
21
54
|
...attrs
|
|
22
55
|
}, ref) => {
|
|
23
56
|
const invert = invertColors ? 'inverse-' : '';
|
|
@@ -42,11 +75,13 @@ const IconButton = React.forwardRef(({
|
|
|
42
75
|
{...attrs}
|
|
43
76
|
>
|
|
44
77
|
<span className="btn-icon__icon-container">
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
78
|
+
{IconComponent && (
|
|
79
|
+
<IconComponent
|
|
80
|
+
className={classNames('btn-icon__icon', iconClassNames)}
|
|
81
|
+
icon={icon as any}
|
|
82
|
+
src={src}
|
|
83
|
+
/>
|
|
84
|
+
)}
|
|
50
85
|
</span>
|
|
51
86
|
</button>
|
|
52
87
|
);
|
|
@@ -54,7 +89,7 @@ const IconButton = React.forwardRef(({
|
|
|
54
89
|
|
|
55
90
|
IconButton.defaultProps = {
|
|
56
91
|
iconAs: Icon,
|
|
57
|
-
src:
|
|
92
|
+
src: undefined,
|
|
58
93
|
icon: undefined,
|
|
59
94
|
iconClassNames: undefined,
|
|
60
95
|
className: undefined,
|
|
@@ -63,17 +98,18 @@ IconButton.defaultProps = {
|
|
|
63
98
|
size: 'md',
|
|
64
99
|
onClick: () => {},
|
|
65
100
|
isActive: false,
|
|
101
|
+
children: undefined,
|
|
66
102
|
};
|
|
67
103
|
|
|
68
104
|
IconButton.propTypes = {
|
|
69
105
|
/** A custom class name. */
|
|
70
106
|
className: PropTypes.string,
|
|
71
107
|
/** Component that renders the icon, currently defaults to `Icon` */
|
|
72
|
-
iconAs: PropTypes.elementType,
|
|
108
|
+
iconAs: PropTypes.elementType as any,
|
|
73
109
|
/** An icon component to render. Example import of a Paragon icon component:
|
|
74
|
-
* `import { Check } from '@openedx/paragon/
|
|
110
|
+
* `import { Check } from '@openedx/paragon/icons';`
|
|
75
111
|
* */
|
|
76
|
-
src: PropTypes.
|
|
112
|
+
src: PropTypes.elementType as any,
|
|
77
113
|
/** Alt text for your icon. For best practice, avoid using alt text to describe
|
|
78
114
|
* the image in the `IconButton`. Instead, we recommend describing the function
|
|
79
115
|
* of the button. */
|
|
@@ -86,7 +122,7 @@ IconButton.propTypes = {
|
|
|
86
122
|
iconName: PropTypes.string,
|
|
87
123
|
// eslint-disable-next-line react/forbid-prop-types
|
|
88
124
|
icon: PropTypes.array,
|
|
89
|
-
}),
|
|
125
|
+
}) as any,
|
|
90
126
|
/** Extra class names that will be added to the icon */
|
|
91
127
|
iconClassNames: PropTypes.string,
|
|
92
128
|
/** Click handler for the button */
|
|
@@ -99,38 +135,40 @@ IconButton.propTypes = {
|
|
|
99
135
|
isActive: PropTypes.bool,
|
|
100
136
|
};
|
|
101
137
|
|
|
138
|
+
interface PropsWithTooltip extends Props {
|
|
139
|
+
/** choose from https://popper.js.org/docs/v2/constructors/#options */
|
|
140
|
+
tooltipPlacement: Placement,
|
|
141
|
+
/** any content to pass to tooltip content area */
|
|
142
|
+
tooltipContent: React.ReactNode,
|
|
143
|
+
}
|
|
144
|
+
|
|
102
145
|
/**
|
|
103
|
-
*
|
|
104
|
-
* @param { object } args Arguments
|
|
105
|
-
* @param { string } args.tooltipPlacement choose from https://popper.js.org/docs/v2/constructors/#options
|
|
106
|
-
* @param { React.Component } args.tooltipContent any content to pass to tooltip content area
|
|
107
|
-
* @returns { IconButton } a button wrapped in overlaytrigger
|
|
146
|
+
* An icon button wrapped in overlaytrigger to display a tooltip.
|
|
108
147
|
*/
|
|
109
148
|
function IconButtonWithTooltip({
|
|
110
|
-
tooltipPlacement, tooltipContent,
|
|
111
|
-
}) {
|
|
112
|
-
const invert = invertColors ? 'inverse-' : '';
|
|
149
|
+
tooltipPlacement, tooltipContent, ...props
|
|
150
|
+
}: PropsWithTooltip) {
|
|
151
|
+
const invert = props.invertColors ? 'inverse-' : '';
|
|
113
152
|
return (
|
|
114
153
|
<OverlayTrigger
|
|
115
154
|
placement={tooltipPlacement}
|
|
116
155
|
overlay={(
|
|
117
156
|
<Tooltip
|
|
118
157
|
id={`iconbutton-tooltip-${tooltipPlacement}`}
|
|
119
|
-
variant={invert ? 'light' :
|
|
158
|
+
variant={invert ? 'light' : undefined}
|
|
120
159
|
>
|
|
121
160
|
{tooltipContent}
|
|
122
161
|
</Tooltip>
|
|
123
162
|
)}
|
|
124
163
|
>
|
|
125
|
-
<IconButton
|
|
164
|
+
<IconButton {...props} />
|
|
126
165
|
</OverlayTrigger>
|
|
127
166
|
);
|
|
128
167
|
}
|
|
129
168
|
|
|
130
169
|
IconButtonWithTooltip.defaultProps = {
|
|
170
|
+
...IconButton.defaultProps,
|
|
131
171
|
tooltipPlacement: 'top',
|
|
132
|
-
variant: 'primary',
|
|
133
|
-
invertColors: false,
|
|
134
172
|
};
|
|
135
173
|
|
|
136
174
|
IconButtonWithTooltip.propTypes = {
|
|
@@ -144,7 +182,9 @@ IconButtonWithTooltip.propTypes = {
|
|
|
144
182
|
invertColors: PropTypes.bool,
|
|
145
183
|
};
|
|
146
184
|
|
|
147
|
-
IconButton.IconButtonWithTooltip = IconButtonWithTooltip;
|
|
185
|
+
(IconButton as any).IconButtonWithTooltip = IconButtonWithTooltip;
|
|
148
186
|
|
|
149
|
-
export default IconButton
|
|
187
|
+
export default IconButton as typeof IconButton & {
|
|
188
|
+
IconButtonWithTooltip: typeof IconButtonWithTooltip,
|
|
189
|
+
};
|
|
150
190
|
export { IconButtonWithTooltip };
|
package/src/Modal/ModalPopup.jsx
CHANGED
|
@@ -34,6 +34,14 @@ function ModalPopup({
|
|
|
34
34
|
},
|
|
35
35
|
];
|
|
36
36
|
|
|
37
|
+
const handleOnClickOutside = (e) => {
|
|
38
|
+
if (e.type === 'touchstart') {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
onClose();
|
|
43
|
+
};
|
|
44
|
+
|
|
37
45
|
return (
|
|
38
46
|
<ModalContextProvider onClose={onClose} isOpen={isOpen} isBlocking={isBlocking}>
|
|
39
47
|
<RootComponent>
|
|
@@ -47,7 +55,7 @@ function ModalPopup({
|
|
|
47
55
|
scrollLock={false}
|
|
48
56
|
enabled={isOpen}
|
|
49
57
|
onEscapeKey={onClose}
|
|
50
|
-
onClickOutside={
|
|
58
|
+
onClickOutside={handleOnClickOutside}
|
|
51
59
|
>
|
|
52
60
|
{isOpen && (
|
|
53
61
|
<div className="pgn__modal-popup__tooltip">
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { fireEvent, render } from '@testing-library/react';
|
|
3
|
+
import userEvent from '@testing-library/user-event';
|
|
4
|
+
import ModalPopup from '../ModalPopup';
|
|
5
|
+
|
|
6
|
+
describe('<ModalPopup />', () => {
|
|
7
|
+
const mockPositionRef = React.createRef();
|
|
8
|
+
|
|
9
|
+
describe('when isOpen', () => {
|
|
10
|
+
const isOpen = true;
|
|
11
|
+
const closeFn = jest.fn();
|
|
12
|
+
|
|
13
|
+
it('calls close on click events but not touchstart events', async () => {
|
|
14
|
+
render(
|
|
15
|
+
<ModalPopup
|
|
16
|
+
positionRef={mockPositionRef}
|
|
17
|
+
isOpen={isOpen}
|
|
18
|
+
onClose={closeFn}
|
|
19
|
+
>
|
|
20
|
+
<div>Modal Contents</div>
|
|
21
|
+
</ModalPopup>,
|
|
22
|
+
);
|
|
23
|
+
await fireEvent.touchStart(document.body);
|
|
24
|
+
expect(closeFn).not.toHaveBeenCalled();
|
|
25
|
+
await userEvent.click(document.body);
|
|
26
|
+
expect(closeFn).toHaveBeenCalled();
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
});
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import BaseOverlay from 'react-bootstrap/Overlay';
|
|
3
|
-
import BaseOverlayTrigger from 'react-bootstrap/OverlayTrigger';
|
|
2
|
+
import BaseOverlay, { type OverlayProps, type Placement } from 'react-bootstrap/Overlay';
|
|
3
|
+
import BaseOverlayTrigger, { type OverlayTriggerProps, type OverlayTriggerType } from 'react-bootstrap/OverlayTrigger';
|
|
4
|
+
import Fade from 'react-bootstrap/Fade';
|
|
4
5
|
import PropTypes from 'prop-types';
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
// Note: The only thing this file adds to the base component is propTypes validation.
|
|
8
|
+
// As more Paragon consumers adopt TypeScript, we could consider removing almost all of this code
|
|
9
|
+
// and just re-export the Overlay and OverlayTrigger components from react-bootstrap unmodified.
|
|
10
|
+
|
|
11
|
+
const PLACEMENT_VARIANTS: Placement[] = [
|
|
7
12
|
'auto-start',
|
|
8
13
|
'auto',
|
|
9
14
|
'auto-end',
|
|
@@ -21,16 +26,16 @@ const PLACEMENT_VARIANTS = [
|
|
|
21
26
|
'left-start',
|
|
22
27
|
];
|
|
23
28
|
|
|
24
|
-
const TRIGGER_VARIANTS = [
|
|
29
|
+
const TRIGGER_VARIANTS: OverlayTriggerType[] = [
|
|
25
30
|
'hover',
|
|
26
31
|
'click',
|
|
27
32
|
'focus',
|
|
28
33
|
];
|
|
29
34
|
|
|
30
|
-
function Overlay(props) {
|
|
35
|
+
function Overlay(props: OverlayProps) {
|
|
31
36
|
return <BaseOverlay {...props} />;
|
|
32
37
|
}
|
|
33
|
-
function OverlayTrigger(props) {
|
|
38
|
+
function OverlayTrigger(props: OverlayTriggerProps) {
|
|
34
39
|
return (
|
|
35
40
|
<BaseOverlayTrigger {...props}>
|
|
36
41
|
{props.children}
|
|
@@ -88,7 +93,7 @@ Overlay.propTypes = {
|
|
|
88
93
|
* Animate the entering and exiting of the Overlay. `true` will use the `<Fade>` transition,
|
|
89
94
|
* or a custom react-transition-group `<Transition>` component can be provided.
|
|
90
95
|
*/
|
|
91
|
-
transition: PropTypes.oneOfType([PropTypes.
|
|
96
|
+
transition: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]),
|
|
92
97
|
};
|
|
93
98
|
|
|
94
99
|
OverlayTrigger.propTypes = {
|
|
@@ -144,7 +149,7 @@ Overlay.defaultProps = {
|
|
|
144
149
|
rootCloseEvent: undefined,
|
|
145
150
|
show: false,
|
|
146
151
|
target: undefined,
|
|
147
|
-
transition:
|
|
152
|
+
transition: Fade,
|
|
148
153
|
};
|
|
149
154
|
|
|
150
155
|
OverlayTrigger.defaultProps = {
|
package/src/Stepper/index.scss
CHANGED
|
@@ -8,8 +8,9 @@
|
|
|
8
8
|
|
|
9
9
|
.pgn__stepper-header-step-list {
|
|
10
10
|
list-style: none;
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
padding:
|
|
12
|
+
var(--pgn-spacing-stepper-header-step-list-padding-y)
|
|
13
|
+
var(--pgn-spacing-stepper-header-step-list-padding-x);
|
|
13
14
|
display: flex;
|
|
14
15
|
align-items: center;
|
|
15
16
|
margin: var(--pgn-spacing-stepper-header-step-list-margin);
|
package/src/Tabs/index.scss
CHANGED
|
@@ -30,8 +30,9 @@
|
|
|
30
30
|
margin: 0;
|
|
31
31
|
|
|
32
32
|
.dropdown .dropdown-toggle {
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
padding:
|
|
34
|
+
var(--pgn-spacing-tab-more-link-dropdown-toggle-padding-x)
|
|
35
|
+
var(--pgn-spacing-tab-more-link-dropdown-toggle-padding-y);
|
|
35
36
|
|
|
36
37
|
&:focus {
|
|
37
38
|
background-color: var(--pgn-color-tab-more-link-dropdown-toggle-bg-focus);
|
|
@@ -62,8 +63,9 @@
|
|
|
62
63
|
// Nav inverse pills
|
|
63
64
|
&.nav-inverse-pills .pgn__tab_more.nav-link {
|
|
64
65
|
.dropdown .dropdown-toggle {
|
|
65
|
-
|
|
66
|
-
|
|
66
|
+
padding:
|
|
67
|
+
var(--pgn-spacing-tab-inverse-pills-link-dropdown-toggle-padding-x)
|
|
68
|
+
var(--pgn-spacing-tab-inverse-pills-link-dropdown-toggle-padding-y);
|
|
67
69
|
border: none;
|
|
68
70
|
|
|
69
71
|
&:focus {
|
|
@@ -97,8 +99,9 @@
|
|
|
97
99
|
|
|
98
100
|
// Nav inverse tabs
|
|
99
101
|
&.nav-inverse-tabs .pgn__tab_more.nav-link .dropdown .dropdown-toggle {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
+
padding:
|
|
103
|
+
var(--pgn-spacing-tab-inverse-tabs-link-dropdown-toggle-padding-x)
|
|
104
|
+
var(--pgn-spacing-tab-inverse-tabs-link-dropdown-toggle-padding-y);
|
|
102
105
|
|
|
103
106
|
&:hover {
|
|
104
107
|
background-color: var(--pgn-color-tab-inverse-pills-link-dropdown-toggle-bg-hover);
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import classNames from 'classnames';
|
|
4
|
-
import BaseTooltip from 'react-bootstrap/Tooltip';
|
|
4
|
+
import BaseTooltip, { type TooltipProps as BaseTooltipProps } from 'react-bootstrap/Tooltip';
|
|
5
|
+
import { type Placement } from 'react-bootstrap/Overlay';
|
|
6
|
+
import type { ComponentWithAsProp } from '../utils/types/bootstrap';
|
|
5
7
|
|
|
6
|
-
|
|
8
|
+
interface TooltipProps extends BaseTooltipProps {
|
|
9
|
+
variant?: 'light';
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const PLACEMENT_VARIANTS: Placement[] = [
|
|
7
13
|
'auto-start',
|
|
8
14
|
'auto',
|
|
9
15
|
'auto-end',
|
|
@@ -21,7 +27,7 @@ const PLACEMENT_VARIANTS = [
|
|
|
21
27
|
'left-start',
|
|
22
28
|
];
|
|
23
29
|
|
|
24
|
-
const Tooltip = React.forwardRef(({
|
|
30
|
+
const Tooltip: ComponentWithAsProp<'div', TooltipProps> = React.forwardRef(({
|
|
25
31
|
children,
|
|
26
32
|
variant,
|
|
27
33
|
...props
|