@lumx/react 2.1.3 → 2.1.6-alpha.1
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/esm/_internal/Avatar2.js +5 -1
- package/esm/_internal/Avatar2.js.map +1 -1
- package/esm/_internal/Dialog2.js +13 -8
- package/esm/_internal/Dialog2.js.map +1 -1
- package/esm/_internal/DragHandle.js +1 -1
- package/esm/_internal/DragHandle.js.map +1 -1
- package/esm/_internal/Flag2.js +1 -3
- package/esm/_internal/Flag2.js.map +1 -1
- package/esm/_internal/Message2.js +2 -2
- package/esm/_internal/Message2.js.map +1 -1
- package/esm/_internal/SlideshowControls.js +2 -2
- package/esm/_internal/SlideshowControls.js.map +1 -1
- package/esm/_internal/Thumbnail2.js +29 -34
- package/esm/_internal/Thumbnail2.js.map +1 -1
- package/esm/_internal/UserBlock.js +44 -14
- package/esm/_internal/UserBlock.js.map +1 -1
- package/esm/_internal/user-block.js +1 -0
- package/esm/_internal/user-block.js.map +1 -1
- package/package.json +16 -17
- package/src/components/avatar/Avatar.tsx +8 -0
- package/src/components/dialog/Dialog.stories.tsx +44 -2
- package/src/components/dialog/Dialog.tsx +15 -11
- package/src/components/dialog/__snapshots__/Dialog.test.tsx.snap +76 -0
- package/src/components/drag-handle/DragHandle.tsx +5 -1
- package/src/components/flag/Flag.test.tsx +1 -2
- package/src/components/flag/Flag.tsx +2 -10
- package/src/components/flag/__snapshots__/Flag.test.tsx.snap +0 -15
- package/src/components/message/Message.tsx +2 -2
- package/src/components/slideshow/useKeyNavigate.ts +2 -2
- package/src/components/thumbnail/Thumbnail.stories.tsx +21 -0
- package/src/components/thumbnail/Thumbnail.test.tsx +20 -2
- package/src/components/thumbnail/Thumbnail.tsx +40 -15
- package/src/components/thumbnail/__snapshots__/Thumbnail.test.tsx.snap +53 -6
- package/src/components/user-block/UserBlock.stories.tsx +27 -4
- package/src/components/user-block/UserBlock.tsx +40 -16
- package/src/components/user-block/__snapshots__/UserBlock.test.tsx.snap +244 -145
- package/types.d.ts +12 -0
|
@@ -36,6 +36,10 @@ export interface AvatarProps extends GenericProps {
|
|
|
36
36
|
ThumbnailProps,
|
|
37
37
|
'image' | 'alt' | 'size' | 'theme' | 'align' | 'fillHeight' | 'variant' | 'aspectRatio'
|
|
38
38
|
>;
|
|
39
|
+
/** Props to pass to the link wrapping the thumbnail. */
|
|
40
|
+
linkProps?: React.DetailedHTMLProps<React.AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>;
|
|
41
|
+
/** Custom react component for the link (can be used to inject react router Link). */
|
|
42
|
+
linkAs?: 'a' | any;
|
|
39
43
|
}
|
|
40
44
|
|
|
41
45
|
/**
|
|
@@ -75,6 +79,8 @@ export const Avatar: Comp<AvatarProps, HTMLDivElement> = forwardRef((props, ref)
|
|
|
75
79
|
size,
|
|
76
80
|
theme,
|
|
77
81
|
thumbnailProps,
|
|
82
|
+
linkProps,
|
|
83
|
+
linkAs,
|
|
78
84
|
...forwardedProps
|
|
79
85
|
} = props;
|
|
80
86
|
|
|
@@ -85,6 +91,8 @@ export const Avatar: Comp<AvatarProps, HTMLDivElement> = forwardRef((props, ref)
|
|
|
85
91
|
className={classNames(className, handleBasicClasses({ prefix: CLASSNAME, size, theme }))}
|
|
86
92
|
>
|
|
87
93
|
<Thumbnail
|
|
94
|
+
linkProps={linkProps}
|
|
95
|
+
linkAs={linkAs}
|
|
88
96
|
className={`${CLASSNAME}__thumbnail`}
|
|
89
97
|
onClick={onClick}
|
|
90
98
|
onKeyPress={onKeyPress}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { mdiClose } from '@lumx/icons';
|
|
2
2
|
import {
|
|
3
|
+
AlertDialog,
|
|
3
4
|
Button,
|
|
4
5
|
Checkbox,
|
|
5
6
|
DatePickerField,
|
|
@@ -32,9 +33,9 @@ const content = <div className="lumx-spacing-padding">{loremIpsum('short')}</div
|
|
|
32
33
|
const longContent = <div className="lumx-spacing-padding">{loremIpsum('long')}</div>;
|
|
33
34
|
const footer = <footer className="lumx-spacing-padding">Dialog footer</footer>;
|
|
34
35
|
|
|
35
|
-
function useOpenButton(theme: Theme) {
|
|
36
|
+
function useOpenButton(theme: Theme, defaultState = true) {
|
|
36
37
|
const buttonRef = useRef() as RefObject<HTMLButtonElement>;
|
|
37
|
-
const [isOpen, setOpen] = useState(
|
|
38
|
+
const [isOpen, setOpen] = useState(defaultState);
|
|
38
39
|
const openDialog = () => setOpen(true);
|
|
39
40
|
const closeDialog = () => setOpen(false);
|
|
40
41
|
|
|
@@ -45,6 +46,7 @@ function useOpenButton(theme: Theme) {
|
|
|
45
46
|
</Button>
|
|
46
47
|
),
|
|
47
48
|
buttonRef,
|
|
49
|
+
openDialog,
|
|
48
50
|
closeDialog,
|
|
49
51
|
isOpen,
|
|
50
52
|
};
|
|
@@ -83,6 +85,46 @@ export const PreventDialogAutoClose = ({ theme }: any) => {
|
|
|
83
85
|
);
|
|
84
86
|
};
|
|
85
87
|
|
|
88
|
+
export const DialogWithAlertDialog = ({ theme }: any) => {
|
|
89
|
+
const { button, buttonRef, closeDialog, isOpen } = useOpenButton(theme);
|
|
90
|
+
const { openDialog: openAlertDialog, closeDialog: closeAlertDialog, isOpen: isAlertDialogOpen } = useOpenButton(
|
|
91
|
+
theme,
|
|
92
|
+
false,
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
const handleSubmitDialog = () => {
|
|
96
|
+
closeDialog();
|
|
97
|
+
openAlertDialog();
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
return (
|
|
101
|
+
<>
|
|
102
|
+
{button}
|
|
103
|
+
<Dialog isOpen={isOpen} onClose={closeDialog} parentElement={buttonRef}>
|
|
104
|
+
{content}
|
|
105
|
+
<footer>
|
|
106
|
+
<Toolbar
|
|
107
|
+
after={
|
|
108
|
+
<Button onClick={handleSubmitDialog} emphasis={Emphasis.low}>
|
|
109
|
+
Close
|
|
110
|
+
</Button>
|
|
111
|
+
}
|
|
112
|
+
/>
|
|
113
|
+
</footer>
|
|
114
|
+
</Dialog>
|
|
115
|
+
<AlertDialog
|
|
116
|
+
isOpen={isAlertDialogOpen}
|
|
117
|
+
onClose={closeDialog}
|
|
118
|
+
parentElement={buttonRef}
|
|
119
|
+
title="Default (info)"
|
|
120
|
+
confirmProps={{ onClick: closeAlertDialog, label: 'Confirm' }}
|
|
121
|
+
>
|
|
122
|
+
Consequat deserunt officia aute laborum tempor anim sint est.
|
|
123
|
+
</AlertDialog>
|
|
124
|
+
</>
|
|
125
|
+
);
|
|
126
|
+
};
|
|
127
|
+
|
|
86
128
|
export const Sizes = ({ theme }: any) => {
|
|
87
129
|
const { button, buttonRef, closeDialog, isOpen } = useOpenButton(theme);
|
|
88
130
|
const sizes: DialogSizes[] = [Size.tiny, Size.regular, Size.big, Size.huge];
|
|
@@ -115,18 +115,22 @@ export const Dialog: Comp<DialogProps, HTMLDivElement> = forwardRef((props, ref)
|
|
|
115
115
|
...forwardedProps
|
|
116
116
|
} = props;
|
|
117
117
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
118
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
119
|
+
const previousOpen = React.useRef(isOpen);
|
|
120
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
121
|
+
React.useEffect(() => {
|
|
122
|
+
if (isOpen !== previousOpen.current) {
|
|
123
|
+
previousOpen.current = isOpen;
|
|
124
|
+
|
|
125
|
+
// Focus the parent element on close.
|
|
126
|
+
if (!isOpen && parentElement && parentElement.current) {
|
|
127
|
+
parentElement.current.focus();
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}, [isOpen, parentElement]);
|
|
127
131
|
|
|
128
132
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
129
|
-
useCallbackOnEscape(
|
|
133
|
+
useCallbackOnEscape(onClose, isOpen && !preventAutoClose);
|
|
130
134
|
|
|
131
135
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
132
136
|
const wrapperRef = useRef<HTMLDivElement>(null);
|
|
@@ -192,7 +196,7 @@ export const Dialog: Comp<DialogProps, HTMLDivElement> = forwardRef((props, ref)
|
|
|
192
196
|
<div className={`${CLASSNAME}__overlay`} />
|
|
193
197
|
|
|
194
198
|
<section className={`${CLASSNAME}__container`} role="dialog" aria-modal="true" {...dialogProps}>
|
|
195
|
-
<ClickAwayProvider callback={!preventAutoClose &&
|
|
199
|
+
<ClickAwayProvider callback={!preventAutoClose && onClose} refs={clickAwayRefs}>
|
|
196
200
|
<div className={`${CLASSNAME}__wrapper`} ref={wrapperRef}>
|
|
197
201
|
{(header || headerChildContent) && (
|
|
198
202
|
<header
|
|
@@ -1,5 +1,81 @@
|
|
|
1
1
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
2
|
|
|
3
|
+
exports[`<Dialog> Snapshots and structure should render story DialogWithAlertDialog 1`] = `
|
|
4
|
+
<Fragment>
|
|
5
|
+
<Button
|
|
6
|
+
emphasis="high"
|
|
7
|
+
onClick={[Function]}
|
|
8
|
+
size="m"
|
|
9
|
+
theme="light"
|
|
10
|
+
>
|
|
11
|
+
Open dialog
|
|
12
|
+
</Button>
|
|
13
|
+
<Dialog
|
|
14
|
+
isOpen={true}
|
|
15
|
+
onClose={[Function]}
|
|
16
|
+
parentElement={
|
|
17
|
+
Object {
|
|
18
|
+
"current": undefined,
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
size="big"
|
|
22
|
+
>
|
|
23
|
+
<div
|
|
24
|
+
className="lumx-spacing-padding"
|
|
25
|
+
>
|
|
26
|
+
|
|
27
|
+
Nihil hic munitissimus habendi senatus locus, nihil horum? At nos hinc posthac, sitientis piros
|
|
28
|
+
Afros. Magna pars studiorum, prodita quaerimus. Integer legentibus erat a ante historiarum
|
|
29
|
+
dapibus. Praeterea iter est quasdam res quas ex communi. Ullamco laboris nisi ut aliquid ex ea
|
|
30
|
+
commodi consequat. Inmensae subtilitatis, obscuris et malesuada fames. Me non paenitet nullum
|
|
31
|
+
festiviorem excogitasse ad hoc. Cum ceteris in veneratione tui montes, nascetur mus. Etiam
|
|
32
|
+
habebis sem dicantur magna mollis euismod. Quis aute iure reprehenderit in voluptate velit esse.
|
|
33
|
+
Phasellus laoreet lorem vel dolor tempus vehicula. Ambitioni dedisse scripsisse iudicaretur.
|
|
34
|
+
Paullum deliquit, ponderibus modulisque suis ratio utitur. Ab illo tempore, ab est sed
|
|
35
|
+
immemorabili. Nec dubitamus multa iter quae et nos invenerat. Tu quoque, Brute, fili mi, nihil
|
|
36
|
+
timor populi, nihil! Morbi fringilla convallis sapien, id pulvinar odio volutpat. Cras mattis
|
|
37
|
+
iudicium purus sit amet fermentum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus.
|
|
38
|
+
Quisque ut dolor gravida, placerat libero vel, euismod. Unam incolunt Belgae, aliam Aquitani,
|
|
39
|
+
tertiam. Cras mattis iudicium purus sit amet fermentum
|
|
40
|
+
</div>
|
|
41
|
+
<footer>
|
|
42
|
+
<Toolbar
|
|
43
|
+
after={
|
|
44
|
+
<Button
|
|
45
|
+
emphasis="low"
|
|
46
|
+
onClick={[Function]}
|
|
47
|
+
size="m"
|
|
48
|
+
theme="light"
|
|
49
|
+
>
|
|
50
|
+
Close
|
|
51
|
+
</Button>
|
|
52
|
+
}
|
|
53
|
+
/>
|
|
54
|
+
</footer>
|
|
55
|
+
</Dialog>
|
|
56
|
+
<AlertDialog
|
|
57
|
+
confirmProps={
|
|
58
|
+
Object {
|
|
59
|
+
"label": "Confirm",
|
|
60
|
+
"onClick": [Function],
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
isOpen={false}
|
|
64
|
+
kind="info"
|
|
65
|
+
onClose={[Function]}
|
|
66
|
+
parentElement={
|
|
67
|
+
Object {
|
|
68
|
+
"current": undefined,
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
size="tiny"
|
|
72
|
+
title="Default (info)"
|
|
73
|
+
>
|
|
74
|
+
Consequat deserunt officia aute laborum tempor anim sint est.
|
|
75
|
+
</AlertDialog>
|
|
76
|
+
</Fragment>
|
|
77
|
+
`;
|
|
78
|
+
|
|
3
79
|
exports[`<Dialog> Snapshots and structure should render story DialogWithFocusableElements 1`] = `
|
|
4
80
|
<Fragment>
|
|
5
81
|
<Button
|
|
@@ -40,7 +40,11 @@ export const DragHandle: Comp<DragHandleProps, HTMLDivElement> = forwardRef((pro
|
|
|
40
40
|
{...forwardedProps}
|
|
41
41
|
className={classNames(className, handleBasicClasses({ prefix: CLASSNAME, theme }))}
|
|
42
42
|
>
|
|
43
|
-
<Icon
|
|
43
|
+
<Icon
|
|
44
|
+
icon={mdiDragVertical}
|
|
45
|
+
color={theme === Theme.dark ? ColorPalette.light : ColorPalette.dark}
|
|
46
|
+
size={Size.xs}
|
|
47
|
+
/>
|
|
44
48
|
</div>
|
|
45
49
|
);
|
|
46
50
|
});
|
|
@@ -69,9 +69,8 @@ describe(`<${Flag.displayName} />`, () => {
|
|
|
69
69
|
|
|
70
70
|
it('should use the color', () => {
|
|
71
71
|
const color = ColorPalette.green;
|
|
72
|
-
const { wrapper
|
|
72
|
+
const { wrapper } = setup({ icon: mdiAbTesting, color });
|
|
73
73
|
|
|
74
|
-
expect(iconEl.prop('color')).toEqual(color);
|
|
75
74
|
expect(wrapper).toHaveClassName(
|
|
76
75
|
getBasicClass({
|
|
77
76
|
prefix: CLASSNAME,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { forwardRef } from 'react';
|
|
2
2
|
import classNames from 'classnames';
|
|
3
3
|
|
|
4
|
-
import { ColorPalette,
|
|
4
|
+
import { ColorPalette, Icon, Size, Theme } from '@lumx/react';
|
|
5
5
|
import { Comp, GenericProps, getRootClassName, handleBasicClasses } from '@lumx/react/utils';
|
|
6
6
|
|
|
7
7
|
export interface FlagProps extends GenericProps {
|
|
@@ -38,15 +38,7 @@ export const Flag: Comp<FlagProps, HTMLDivElement> = forwardRef((props, ref) =>
|
|
|
38
38
|
className={classNames(className, handleBasicClasses({ prefix: CLASSNAME, color: flagColor }))}
|
|
39
39
|
ref={ref}
|
|
40
40
|
>
|
|
41
|
-
{icon &&
|
|
42
|
-
<Icon
|
|
43
|
-
icon={icon}
|
|
44
|
-
color={color}
|
|
45
|
-
colorVariant={ColorVariant.D2}
|
|
46
|
-
size={Size.xxs}
|
|
47
|
-
className={`${CLASSNAME}__icon`}
|
|
48
|
-
/>
|
|
49
|
-
)}
|
|
41
|
+
{icon && <Icon icon={icon} size={Size.xxs} className={`${CLASSNAME}__icon`} />}
|
|
50
42
|
<span className={`${CLASSNAME}__label`}>{label}</span>
|
|
51
43
|
</div>
|
|
52
44
|
);
|
|
@@ -19,8 +19,6 @@ Array [
|
|
|
19
19
|
>
|
|
20
20
|
<Icon
|
|
21
21
|
className="lumx-flag__icon"
|
|
22
|
-
color="blue"
|
|
23
|
-
colorVariant="D2"
|
|
24
22
|
icon="M12,21.35L10.55,20.03C5.4,15.36 2,12.27 2,8.5C2,5.41 4.42,3 7.5,3C9.24,3 10.91,3.81 12,5.08C13.09,3.81 14.76,3 16.5,3C19.58,3 22,5.41 22,8.5C22,12.27 18.6,15.36 13.45,20.03L12,21.35Z"
|
|
25
23
|
size="xxs"
|
|
26
24
|
/>
|
|
@@ -35,8 +33,6 @@ Array [
|
|
|
35
33
|
>
|
|
36
34
|
<Icon
|
|
37
35
|
className="lumx-flag__icon"
|
|
38
|
-
color="dark"
|
|
39
|
-
colorVariant="D2"
|
|
40
36
|
icon="M12,21.35L10.55,20.03C5.4,15.36 2,12.27 2,8.5C2,5.41 4.42,3 7.5,3C9.24,3 10.91,3.81 12,5.08C13.09,3.81 14.76,3 16.5,3C19.58,3 22,5.41 22,8.5C22,12.27 18.6,15.36 13.45,20.03L12,21.35Z"
|
|
41
37
|
size="xxs"
|
|
42
38
|
/>
|
|
@@ -51,8 +47,6 @@ Array [
|
|
|
51
47
|
>
|
|
52
48
|
<Icon
|
|
53
49
|
className="lumx-flag__icon"
|
|
54
|
-
color="green"
|
|
55
|
-
colorVariant="D2"
|
|
56
50
|
icon="M12,21.35L10.55,20.03C5.4,15.36 2,12.27 2,8.5C2,5.41 4.42,3 7.5,3C9.24,3 10.91,3.81 12,5.08C13.09,3.81 14.76,3 16.5,3C19.58,3 22,5.41 22,8.5C22,12.27 18.6,15.36 13.45,20.03L12,21.35Z"
|
|
57
51
|
size="xxs"
|
|
58
52
|
/>
|
|
@@ -67,8 +61,6 @@ Array [
|
|
|
67
61
|
>
|
|
68
62
|
<Icon
|
|
69
63
|
className="lumx-flag__icon"
|
|
70
|
-
color="primary"
|
|
71
|
-
colorVariant="D2"
|
|
72
64
|
icon="M12,21.35L10.55,20.03C5.4,15.36 2,12.27 2,8.5C2,5.41 4.42,3 7.5,3C9.24,3 10.91,3.81 12,5.08C13.09,3.81 14.76,3 16.5,3C19.58,3 22,5.41 22,8.5C22,12.27 18.6,15.36 13.45,20.03L12,21.35Z"
|
|
73
65
|
size="xxs"
|
|
74
66
|
/>
|
|
@@ -83,8 +75,6 @@ Array [
|
|
|
83
75
|
>
|
|
84
76
|
<Icon
|
|
85
77
|
className="lumx-flag__icon"
|
|
86
|
-
color="red"
|
|
87
|
-
colorVariant="D2"
|
|
88
78
|
icon="M12,21.35L10.55,20.03C5.4,15.36 2,12.27 2,8.5C2,5.41 4.42,3 7.5,3C9.24,3 10.91,3.81 12,5.08C13.09,3.81 14.76,3 16.5,3C19.58,3 22,5.41 22,8.5C22,12.27 18.6,15.36 13.45,20.03L12,21.35Z"
|
|
89
79
|
size="xxs"
|
|
90
80
|
/>
|
|
@@ -99,8 +89,6 @@ Array [
|
|
|
99
89
|
>
|
|
100
90
|
<Icon
|
|
101
91
|
className="lumx-flag__icon"
|
|
102
|
-
color="secondary"
|
|
103
|
-
colorVariant="D2"
|
|
104
92
|
icon="M12,21.35L10.55,20.03C5.4,15.36 2,12.27 2,8.5C2,5.41 4.42,3 7.5,3C9.24,3 10.91,3.81 12,5.08C13.09,3.81 14.76,3 16.5,3C19.58,3 22,5.41 22,8.5C22,12.27 18.6,15.36 13.45,20.03L12,21.35Z"
|
|
105
93
|
size="xxs"
|
|
106
94
|
/>
|
|
@@ -115,8 +103,6 @@ Array [
|
|
|
115
103
|
>
|
|
116
104
|
<Icon
|
|
117
105
|
className="lumx-flag__icon"
|
|
118
|
-
color="yellow"
|
|
119
|
-
colorVariant="D2"
|
|
120
106
|
icon="M12,21.35L10.55,20.03C5.4,15.36 2,12.27 2,8.5C2,5.41 4.42,3 7.5,3C9.24,3 10.91,3.81 12,5.08C13.09,3.81 14.76,3 16.5,3C19.58,3 22,5.41 22,8.5C22,12.27 18.6,15.36 13.45,20.03L12,21.35Z"
|
|
121
107
|
size="xxs"
|
|
122
108
|
/>
|
|
@@ -135,7 +121,6 @@ exports[`<Flag /> Snapshots and structure should render story 'withIcon' 1`] = `
|
|
|
135
121
|
>
|
|
136
122
|
<Icon
|
|
137
123
|
className="lumx-flag__icon"
|
|
138
|
-
colorVariant="D2"
|
|
139
124
|
icon="M12,21.35L10.55,20.03C5.4,15.36 2,12.27 2,8.5C2,5.41 4.42,3 7.5,3C9.24,3 10.91,3.81 12,5.08C13.09,3.81 14.76,3 16.5,3C19.58,3 22,5.41 22,8.5C22,12.27 18.6,15.36 13.45,20.03L12,21.35Z"
|
|
140
125
|
size="xxs"
|
|
141
126
|
/>
|
|
@@ -62,8 +62,8 @@ export const Message: Comp<MessageProps, HTMLDivElement> = forwardRef((props, re
|
|
|
62
62
|
)}
|
|
63
63
|
{...forwardedProps}
|
|
64
64
|
>
|
|
65
|
-
{(customIcon || icon) && <Icon className=
|
|
66
|
-
<div className=
|
|
65
|
+
{(customIcon || icon) && <Icon className={`${CLASSNAME}__icon`} icon={customIcon || icon} size={Size.xs} />}
|
|
66
|
+
<div className={`${CLASSNAME}__text`}>{children}</div>
|
|
67
67
|
</div>
|
|
68
68
|
);
|
|
69
69
|
});
|
|
@@ -9,9 +9,9 @@ export function useKeyNavigate(element?: HTMLElement | null, onNext?: () => void
|
|
|
9
9
|
const onKeyNavigate = (evt: KeyboardEvent) => {
|
|
10
10
|
let callback;
|
|
11
11
|
if (evt?.key === 'ArrowRight') {
|
|
12
|
-
callback = onPrevious;
|
|
13
|
-
} else if (evt?.key === 'ArrowLeft') {
|
|
14
12
|
callback = onNext;
|
|
13
|
+
} else if (evt?.key === 'ArrowLeft') {
|
|
14
|
+
callback = onPrevious;
|
|
15
15
|
}
|
|
16
16
|
if (!callback) return;
|
|
17
17
|
|
|
@@ -18,6 +18,7 @@ import { boolean, select, text } from '@storybook/addon-knobs';
|
|
|
18
18
|
import { enumKnob } from '@lumx/react/stories/knobs/enumKnob';
|
|
19
19
|
import { focusKnob } from '@lumx/react/stories/knobs/focusKnob';
|
|
20
20
|
import { sizeKnob } from '@lumx/react/stories/knobs/sizeKnob';
|
|
21
|
+
import classNames from 'classnames';
|
|
21
22
|
|
|
22
23
|
export default { title: 'LumX components/thumbnail/Thumbnail' };
|
|
23
24
|
|
|
@@ -25,6 +26,26 @@ export const Default = () => <Thumbnail alt="Image alt text" image={imageKnob()}
|
|
|
25
26
|
|
|
26
27
|
export const Clickable = () => <Thumbnail alt="Click me" image={imageKnob()} size={Size.xxl} onClick={console.log} />;
|
|
27
28
|
|
|
29
|
+
export const ClickableLink = () => (
|
|
30
|
+
<Thumbnail alt="Click me" image={imageKnob()} size={Size.xxl} linkProps={{ href: 'https://google.fr' }} />
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
const CustomLinkComponent = (props: any) => (
|
|
34
|
+
<a {...props} className={classNames('custom-link-component', props.className)}>
|
|
35
|
+
{props.children}
|
|
36
|
+
</a>
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
export const ClickableCustomLink = () => (
|
|
40
|
+
<Thumbnail
|
|
41
|
+
alt="Click me"
|
|
42
|
+
image={imageKnob()}
|
|
43
|
+
size={Size.xxl}
|
|
44
|
+
linkAs={CustomLinkComponent}
|
|
45
|
+
linkProps={{ href: 'https://google.fr', className: 'custom-class-name' }}
|
|
46
|
+
/>
|
|
47
|
+
);
|
|
48
|
+
|
|
28
49
|
export const DefaultFallback = () => <Thumbnail alt="foo" image="foo" />;
|
|
29
50
|
|
|
30
51
|
export const IconFallback = () => <Thumbnail alt="foo" image="foo" fallback={mdiAbTesting} />;
|
|
@@ -5,7 +5,16 @@ import 'jest-enzyme';
|
|
|
5
5
|
import { commonTestsSuite, itShouldRenderStories } from '@lumx/react/testing/utils';
|
|
6
6
|
|
|
7
7
|
import { Thumbnail, ThumbnailProps } from './Thumbnail';
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
Clickable,
|
|
10
|
+
ClickableCustomLink,
|
|
11
|
+
ClickableLink,
|
|
12
|
+
CustomFallback,
|
|
13
|
+
Default,
|
|
14
|
+
DefaultFallback,
|
|
15
|
+
IconFallback,
|
|
16
|
+
WithBadge,
|
|
17
|
+
} from './Thumbnail.stories';
|
|
9
18
|
|
|
10
19
|
const CLASSNAME = Thumbnail.className as string;
|
|
11
20
|
|
|
@@ -22,7 +31,16 @@ describe(`<${Thumbnail.displayName}>`, () => {
|
|
|
22
31
|
// 1. Test render via snapshot.
|
|
23
32
|
describe('Snapshots and structure', () => {
|
|
24
33
|
itShouldRenderStories(
|
|
25
|
-
{
|
|
34
|
+
{
|
|
35
|
+
Default,
|
|
36
|
+
Clickable,
|
|
37
|
+
ClickableLink,
|
|
38
|
+
ClickableCustomLink,
|
|
39
|
+
DefaultFallback,
|
|
40
|
+
CustomFallback,
|
|
41
|
+
IconFallback,
|
|
42
|
+
WithBadge,
|
|
43
|
+
},
|
|
26
44
|
Thumbnail,
|
|
27
45
|
);
|
|
28
46
|
});
|
|
@@ -20,7 +20,6 @@ import { isInternetExplorer } from '@lumx/react/utils/isInternetExplorer';
|
|
|
20
20
|
import { mergeRefs } from '@lumx/react/utils/mergeRefs';
|
|
21
21
|
import { useFocusPoint } from '@lumx/react/components/thumbnail/useFocusPoint';
|
|
22
22
|
import { useImageLoad } from '@lumx/react/components/thumbnail/useImageLoad';
|
|
23
|
-
import { useClickable } from '@lumx/react/components/thumbnail/useClickable';
|
|
24
23
|
import { FocusPoint, ThumbnailSize, ThumbnailVariant } from './types';
|
|
25
24
|
|
|
26
25
|
type ImgHTMLProps = ImgHTMLAttributes<HTMLImageElement>;
|
|
@@ -63,6 +62,10 @@ export interface ThumbnailProps extends GenericProps {
|
|
|
63
62
|
theme?: Theme;
|
|
64
63
|
/** Variant of the component. */
|
|
65
64
|
variant?: ThumbnailVariant;
|
|
65
|
+
/** Props to pass to the link wrapping the thumbnail. */
|
|
66
|
+
linkProps?: React.DetailedHTMLProps<React.AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>;
|
|
67
|
+
/** Custom react component for the link (can be used to inject react router Link). */
|
|
68
|
+
linkAs?: 'a' | any;
|
|
66
69
|
}
|
|
67
70
|
|
|
68
71
|
/**
|
|
@@ -109,6 +112,8 @@ export const Thumbnail: Comp<ThumbnailProps> = forwardRef((props, ref) => {
|
|
|
109
112
|
size,
|
|
110
113
|
theme,
|
|
111
114
|
variant,
|
|
115
|
+
linkProps,
|
|
116
|
+
linkAs,
|
|
112
117
|
...forwardedProps
|
|
113
118
|
} = props;
|
|
114
119
|
const imgRef = useRef<HTMLImageElement>(null);
|
|
@@ -119,24 +124,44 @@ export const Thumbnail: Comp<ThumbnailProps> = forwardRef((props, ref) => {
|
|
|
119
124
|
const isLoading = loadingState === 'isLoading';
|
|
120
125
|
|
|
121
126
|
const [wrapper, setWrapper] = useState<HTMLElement>();
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
127
|
+
|
|
128
|
+
const isLink = Boolean(linkProps?.href || linkAs);
|
|
129
|
+
const isButton = !!forwardedProps.onClick;
|
|
130
|
+
const isClickable = isButton || isLink;
|
|
131
|
+
|
|
132
|
+
let Wrapper: any = 'div';
|
|
133
|
+
const wrapperProps = { ...forwardedProps };
|
|
134
|
+
if (isLink) {
|
|
135
|
+
Wrapper = linkAs || 'a';
|
|
136
|
+
Object.assign(wrapperProps, linkProps);
|
|
137
|
+
} else if (isButton) {
|
|
138
|
+
Wrapper = 'button';
|
|
139
|
+
}
|
|
134
140
|
|
|
135
141
|
// Update img style according to focus point and aspect ratio.
|
|
136
142
|
const style = useFocusPoint({ image, focusPoint, aspectRatio, imgRef, loadingState, wrapper });
|
|
137
143
|
|
|
138
144
|
return (
|
|
139
|
-
<
|
|
145
|
+
<Wrapper
|
|
146
|
+
{...wrapperProps}
|
|
147
|
+
ref={mergeRefs(setWrapper, ref) as any}
|
|
148
|
+
className={classNames(
|
|
149
|
+
linkProps?.className,
|
|
150
|
+
className,
|
|
151
|
+
handleBasicClasses({
|
|
152
|
+
align,
|
|
153
|
+
aspectRatio,
|
|
154
|
+
prefix: CLASSNAME,
|
|
155
|
+
size,
|
|
156
|
+
theme,
|
|
157
|
+
variant,
|
|
158
|
+
isClickable,
|
|
159
|
+
hasBadge: !!badge,
|
|
160
|
+
}),
|
|
161
|
+
isLoading && wrapper?.getBoundingClientRect()?.height && 'lumx-color-background-dark-L6',
|
|
162
|
+
fillHeight && `${CLASSNAME}--fill-height`,
|
|
163
|
+
)}
|
|
164
|
+
>
|
|
140
165
|
<div
|
|
141
166
|
className={`${CLASSNAME}__background`}
|
|
142
167
|
style={{
|
|
@@ -169,7 +194,7 @@ export const Thumbnail: Comp<ThumbnailProps> = forwardRef((props, ref) => {
|
|
|
169
194
|
))}
|
|
170
195
|
{badge &&
|
|
171
196
|
React.cloneElement(badge, { className: classNames(`${CLASSNAME}__badge`, badge.props.className) })}
|
|
172
|
-
</
|
|
197
|
+
</Wrapper>
|
|
173
198
|
);
|
|
174
199
|
});
|
|
175
200
|
Thumbnail.displayName = COMPONENT_NAME;
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
2
|
|
|
3
3
|
exports[`<Thumbnail> Snapshots and structure should render story 'Clickable' 1`] = `
|
|
4
|
-
<
|
|
5
|
-
className="lumx-thumbnail lumx-thumbnail--size-xxl lumx-thumbnail--theme-light"
|
|
4
|
+
<button
|
|
5
|
+
className="lumx-thumbnail lumx-thumbnail--size-xxl lumx-thumbnail--theme-light lumx-thumbnail--is-clickable"
|
|
6
6
|
onClick={[Function]}
|
|
7
|
-
onKeyPress={[Function]}
|
|
8
|
-
role="button"
|
|
9
|
-
tabIndex={0}
|
|
10
7
|
>
|
|
11
8
|
<div
|
|
12
9
|
className="lumx-thumbnail__background"
|
|
@@ -25,7 +22,57 @@ exports[`<Thumbnail> Snapshots and structure should render story 'Clickable' 1`]
|
|
|
25
22
|
style={Object {}}
|
|
26
23
|
/>
|
|
27
24
|
</div>
|
|
28
|
-
</
|
|
25
|
+
</button>
|
|
26
|
+
`;
|
|
27
|
+
|
|
28
|
+
exports[`<Thumbnail> Snapshots and structure should render story 'ClickableCustomLink' 1`] = `
|
|
29
|
+
<CustomLinkComponent
|
|
30
|
+
className="custom-class-name lumx-thumbnail lumx-thumbnail--size-xxl lumx-thumbnail--theme-light lumx-thumbnail--is-clickable"
|
|
31
|
+
href="https://google.fr"
|
|
32
|
+
>
|
|
33
|
+
<div
|
|
34
|
+
className="lumx-thumbnail__background"
|
|
35
|
+
style={
|
|
36
|
+
Object {
|
|
37
|
+
"display": undefined,
|
|
38
|
+
"visibility": "hidden",
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
>
|
|
42
|
+
<img
|
|
43
|
+
alt="Click me"
|
|
44
|
+
className="lumx-thumbnail__image"
|
|
45
|
+
loading="lazy"
|
|
46
|
+
src="landscape1.jpg"
|
|
47
|
+
style={Object {}}
|
|
48
|
+
/>
|
|
49
|
+
</div>
|
|
50
|
+
</CustomLinkComponent>
|
|
51
|
+
`;
|
|
52
|
+
|
|
53
|
+
exports[`<Thumbnail> Snapshots and structure should render story 'ClickableLink' 1`] = `
|
|
54
|
+
<a
|
|
55
|
+
className="lumx-thumbnail lumx-thumbnail--size-xxl lumx-thumbnail--theme-light lumx-thumbnail--is-clickable"
|
|
56
|
+
href="https://google.fr"
|
|
57
|
+
>
|
|
58
|
+
<div
|
|
59
|
+
className="lumx-thumbnail__background"
|
|
60
|
+
style={
|
|
61
|
+
Object {
|
|
62
|
+
"display": undefined,
|
|
63
|
+
"visibility": "hidden",
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
>
|
|
67
|
+
<img
|
|
68
|
+
alt="Click me"
|
|
69
|
+
className="lumx-thumbnail__image"
|
|
70
|
+
loading="lazy"
|
|
71
|
+
src="landscape1.jpg"
|
|
72
|
+
style={Object {}}
|
|
73
|
+
/>
|
|
74
|
+
</div>
|
|
75
|
+
</a>
|
|
29
76
|
`;
|
|
30
77
|
|
|
31
78
|
exports[`<Thumbnail> Snapshots and structure should render story 'CustomFallback' 1`] = `
|