@ndlib/component-library 0.0.50 → 0.0.52
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/dist/components/composites/DragDropList/index.js +2 -2
- package/dist/components/composites/DropdownLinks/index.js +3 -3
- package/dist/components/composites/Modal/Modal.stories.js +1 -1
- package/dist/components/composites/SnackBar/SnackBar.stories.d.ts +8 -0
- package/dist/components/composites/SnackBar/SnackBar.stories.js +20 -0
- package/dist/components/composites/SnackBar/SnackBar.test.d.ts +1 -0
- package/dist/components/composites/SnackBar/SnackBar.test.js +75 -0
- package/dist/components/composites/SnackBar/examples.d.ts +3 -0
- package/dist/components/composites/SnackBar/examples.js +35 -0
- package/dist/components/composites/SnackBar/index.d.ts +1 -0
- package/dist/components/composites/SnackBar/index.js +45 -0
- package/dist/components/elements/Fields/Select/index.js +6 -6
- package/dist/components/elements/Fields/TextInput/index.js +4 -4
- package/dist/components/elements/ListBox/index.d.ts +1 -1
- package/dist/components/elements/TabList/index.js +2 -2
- package/dist/components/providers/menu.js +3 -3
- package/dist/components/providers/snackBar.d.ts +23 -0
- package/dist/components/providers/snackBar.js +53 -0
- package/dist/components/providers/ui.js +3 -1
- package/dist/components/providers/uniqueIds.d.ts +3 -0
- package/dist/components/providers/uniqueIds.js +23 -0
- package/dist/components/providers/uniqueIds.test.d.ts +1 -0
- package/dist/components/providers/uniqueIds.test.js +26 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +3 -0
- package/dist/theme/custom.d.ts +2 -2
- package/dist/theme/custom.js +2 -2
- package/package.json +2 -2
- package/dist/utils/hooks/useUniqueHtmlId.d.ts +0 -1
- package/dist/utils/hooks/useUniqueHtmlId.js +0 -7
|
@@ -12,16 +12,16 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
12
12
|
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
13
13
|
import { DragDropContext, Droppable, Draggable, } from 'react-beautiful-dnd';
|
|
14
14
|
import DragIndicator from '@mui/icons-material/DragIndicator';
|
|
15
|
-
import { useUniqueHtmlId } from '../../../utils/hooks/useUniqueHtmlId';
|
|
16
15
|
import { BUTTON_TYPE, Button } from '../../elements/Button';
|
|
17
16
|
import { Column } from '../../elements/layout/Column';
|
|
18
17
|
import { Row } from '../../elements/layout/Row';
|
|
18
|
+
import { useUniqueId } from '../../providers/uniqueIds';
|
|
19
19
|
export const DragHandle = (_a) => {
|
|
20
20
|
var { sx } = _a, props = __rest(_a, ["sx"]);
|
|
21
21
|
return (_jsx(Button, Object.assign({ type: BUTTON_TYPE.TEXT, primaryIcon: DragIndicator, disableFocusStyles: true, sx: Object.assign({ cursor: 'inherit' }, sx) }, props)));
|
|
22
22
|
};
|
|
23
23
|
export function DragDropList({ items, itemIdKey = 'id', wrapperStyles, itemStyles, children: renderChild, onReorder, disabled, dragItemsDirectly = false, }) {
|
|
24
|
-
const id =
|
|
24
|
+
const id = useUniqueId('dnd-list');
|
|
25
25
|
return (_jsx(DragDropContext, Object.assign({ onDragStart: (start, provided) => {
|
|
26
26
|
const selectedItem = items.find((item) => String(item[itemIdKey]) === start.draggableId);
|
|
27
27
|
provided.announce(`Selected item: ${selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.accessibleLabel}`);
|
|
@@ -13,16 +13,16 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
13
13
|
import { useCallback, useMemo, useState } from 'react';
|
|
14
14
|
import { autoPlacement, offset, size, useFloating } from '@floating-ui/react';
|
|
15
15
|
import { ListBox } from '../../elements/ListBox';
|
|
16
|
-
import { useUniqueHtmlId } from '../../../utils/hooks/useUniqueHtmlId';
|
|
17
16
|
import { Link } from '../../elements/Link';
|
|
18
17
|
import { Box } from '../../elements/layout/Box';
|
|
19
18
|
import { useComponentConfig } from '../../providers/componentConfig';
|
|
19
|
+
import { useUniqueId } from '../../providers/uniqueIds';
|
|
20
20
|
export const DropdownLinks = (_a) => {
|
|
21
21
|
var { children, options: optionsParam, renderOption: renderOptionParam, openNewTab, role = 'navigation' } = _a, rest = __rest(_a, ["children", "options", "renderOption", "openNewTab", "role"]);
|
|
22
22
|
const [isOpen, setIsOpen] = useState(false);
|
|
23
23
|
const [dropdownMinWidth, setDropdownMinWidth] = useState('0px');
|
|
24
|
-
const listboxId =
|
|
25
|
-
const anchorId =
|
|
24
|
+
const listboxId = useUniqueId('dropdown-links');
|
|
25
|
+
const anchorId = useUniqueId('dropdown-links-anchor');
|
|
26
26
|
const { navigate } = useComponentConfig().link;
|
|
27
27
|
const options = optionsParam.map((option) => (Object.assign(Object.assign({}, option), { onClick: () => {
|
|
28
28
|
if (option.onClick) {
|
|
@@ -14,7 +14,7 @@ const ModalExample = () => {
|
|
|
14
14
|
const modalId = 'modal-example';
|
|
15
15
|
const { open, close, state } = useDialog(modalId);
|
|
16
16
|
const theme = useTheme();
|
|
17
|
-
return (_jsxs(_Fragment, { children: [_jsx(Button, Object.assign({ onClick: open, sx: { position: 'relative', zIndex: theme.zIndex.
|
|
17
|
+
return (_jsxs(_Fragment, { children: [_jsx(Button, Object.assign({ onClick: open, sx: { position: 'relative', zIndex: theme.zIndex.DIALOG } }, { children: "Open Modal" })), _jsx(Modal, Object.assign({ isOpen: state.isOpen, heading: "Heading", close: close, actions: [
|
|
18
18
|
{
|
|
19
19
|
label: 'Secondary',
|
|
20
20
|
isPrimary: false,
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { SnackBar } from './';
|
|
2
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
3
|
+
declare const meta: Meta<typeof SnackBar>;
|
|
4
|
+
export default meta;
|
|
5
|
+
type Story = StoryObj<typeof SnackBar>;
|
|
6
|
+
export declare const Default: Story;
|
|
7
|
+
export declare const ReplaceMessage: Story;
|
|
8
|
+
export declare const WipeMessages: Story;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { SnackBar } from './';
|
|
3
|
+
import { BasicExample, ReplaceMessageExample, WipeMessagesExample, } from './examples';
|
|
4
|
+
const meta = {
|
|
5
|
+
title: 'Composites/SnackBar',
|
|
6
|
+
component: SnackBar,
|
|
7
|
+
};
|
|
8
|
+
export default meta;
|
|
9
|
+
export const Default = {
|
|
10
|
+
render: () => _jsx(BasicExample, {}),
|
|
11
|
+
args: {},
|
|
12
|
+
};
|
|
13
|
+
export const ReplaceMessage = {
|
|
14
|
+
render: () => _jsx(ReplaceMessageExample, {}),
|
|
15
|
+
args: {},
|
|
16
|
+
};
|
|
17
|
+
export const WipeMessages = {
|
|
18
|
+
render: () => _jsx(WipeMessagesExample, {}),
|
|
19
|
+
args: {},
|
|
20
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { act, fireEvent, waitFor } from '@testing-library/react';
|
|
3
|
+
import { vi } from 'vitest';
|
|
4
|
+
import { render } from '../../../utils/test';
|
|
5
|
+
import { BasicExample, ReplaceMessageExample, WipeMessagesExample, } from './examples';
|
|
6
|
+
vi.useFakeTimers();
|
|
7
|
+
describe('SnackBar', () => {
|
|
8
|
+
it('supports adding messages', () => {
|
|
9
|
+
const { getByText } = render(_jsx(BasicExample, {}));
|
|
10
|
+
expect(() => getByText('Message 1')).toThrow();
|
|
11
|
+
const addButton = getByText('Add Message');
|
|
12
|
+
act(() => {
|
|
13
|
+
fireEvent.click(addButton);
|
|
14
|
+
});
|
|
15
|
+
expect(getByText('Message 1')).toBeInTheDocument();
|
|
16
|
+
act(() => {
|
|
17
|
+
fireEvent.click(addButton);
|
|
18
|
+
});
|
|
19
|
+
expect(getByText('Message 1')).toBeInTheDocument();
|
|
20
|
+
expect(getByText('Message 2')).toBeInTheDocument();
|
|
21
|
+
act(() => {
|
|
22
|
+
vi.runAllTimers();
|
|
23
|
+
});
|
|
24
|
+
waitFor(() => {
|
|
25
|
+
expect(() => getByText('Message 1')).toThrow();
|
|
26
|
+
expect(() => getByText('Message 2')).toThrow();
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
it('supports replacing messages', () => {
|
|
30
|
+
const { getByText } = render(_jsx(ReplaceMessageExample, {}));
|
|
31
|
+
expect(() => getByText('Message 1')).toThrow();
|
|
32
|
+
const addButton = getByText('Add Message');
|
|
33
|
+
act(() => {
|
|
34
|
+
fireEvent.click(addButton);
|
|
35
|
+
});
|
|
36
|
+
expect(getByText('Message 1')).toBeInTheDocument();
|
|
37
|
+
act(() => {
|
|
38
|
+
fireEvent.click(addButton);
|
|
39
|
+
});
|
|
40
|
+
expect(() => getByText('Message 1')).toThrow();
|
|
41
|
+
expect(getByText('Message 2')).toBeInTheDocument();
|
|
42
|
+
act(() => {
|
|
43
|
+
vi.runAllTimers();
|
|
44
|
+
});
|
|
45
|
+
waitFor(() => {
|
|
46
|
+
expect(() => getByText('Message 2')).toThrow();
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
it('supports wiping messages', () => {
|
|
50
|
+
const { getByText } = render(_jsx(WipeMessagesExample, {}));
|
|
51
|
+
expect(() => getByText('Message 1')).toThrow();
|
|
52
|
+
const addButton = getByText('Add Message');
|
|
53
|
+
act(() => {
|
|
54
|
+
fireEvent.click(addButton);
|
|
55
|
+
});
|
|
56
|
+
act(() => {
|
|
57
|
+
fireEvent.click(addButton);
|
|
58
|
+
});
|
|
59
|
+
expect(getByText('Message 1')).toBeInTheDocument();
|
|
60
|
+
expect(getByText('Message 2')).toBeInTheDocument();
|
|
61
|
+
const wipeButton = getByText('Wipe and Add');
|
|
62
|
+
act(() => {
|
|
63
|
+
fireEvent.click(wipeButton);
|
|
64
|
+
});
|
|
65
|
+
expect(() => getByText('Message 1')).toThrow();
|
|
66
|
+
expect(() => getByText('Message 2')).toThrow();
|
|
67
|
+
expect(getByText('Message 3')).toBeInTheDocument();
|
|
68
|
+
act(() => {
|
|
69
|
+
vi.runAllTimers();
|
|
70
|
+
});
|
|
71
|
+
waitFor(() => {
|
|
72
|
+
expect(() => getByText('Message 3')).toThrow();
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { useSnackBar } from '../../providers/snackBar';
|
|
4
|
+
import { Row } from '../../elements/layout/Row';
|
|
5
|
+
import { Button } from '../../elements/Button';
|
|
6
|
+
import { SnackBar } from '.';
|
|
7
|
+
export const BasicExample = () => {
|
|
8
|
+
const { addMessage } = useSnackBar();
|
|
9
|
+
const [messageCount, setMessageCount] = useState(1);
|
|
10
|
+
return (_jsxs(_Fragment, { children: [_jsx(Button, Object.assign({ onClick: () => {
|
|
11
|
+
setMessageCount(messageCount + 1);
|
|
12
|
+
addMessage({ message: `Message ${messageCount}` });
|
|
13
|
+
} }, { children: "Add Message" })), _jsx(SnackBar, {})] }));
|
|
14
|
+
};
|
|
15
|
+
export const ReplaceMessageExample = () => {
|
|
16
|
+
const { addMessage } = useSnackBar();
|
|
17
|
+
const [messageCount, setMessageCount] = useState(1);
|
|
18
|
+
return (_jsxs(_Fragment, { children: [_jsx(Button, Object.assign({ onClick: () => {
|
|
19
|
+
setMessageCount(messageCount + 1);
|
|
20
|
+
addMessage({ message: `Message ${messageCount}`, id: 'same-id' });
|
|
21
|
+
} }, { children: "Add Message" })), _jsx(SnackBar, {})] }));
|
|
22
|
+
};
|
|
23
|
+
export const WipeMessagesExample = () => {
|
|
24
|
+
const { addMessage } = useSnackBar();
|
|
25
|
+
const [messageCount, setMessageCount] = useState(1);
|
|
26
|
+
return (_jsxs(Row, { children: [_jsx(Button, Object.assign({ onClick: () => {
|
|
27
|
+
setMessageCount((count) => count + 1);
|
|
28
|
+
addMessage({ message: `Message ${messageCount}` });
|
|
29
|
+
}, sx: { mr: 1 } }, { children: "Add Message" })), _jsx(Button, Object.assign({ onClick: () => {
|
|
30
|
+
setMessageCount((count) => count + 1);
|
|
31
|
+
addMessage({ message: `Message ${messageCount}` }, {
|
|
32
|
+
wipePrevious: true,
|
|
33
|
+
});
|
|
34
|
+
} }, { children: "Wipe and Add" })), _jsx(SnackBar, {})] }));
|
|
35
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const SnackBar: () => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { keyframes } from '@emotion/react';
|
|
3
|
+
import { COLOR } from '../../../theme/colors';
|
|
4
|
+
import { TYPOGRAPHY_TYPE, getTypographyStyles } from '../../../theme/typography';
|
|
5
|
+
import { BUTTON_TYPE, Button } from '../../elements/Button';
|
|
6
|
+
import { Box } from '../../elements/layout/Box';
|
|
7
|
+
import { Column } from '../../elements/layout/Column';
|
|
8
|
+
import { Row } from '../../elements/layout/Row';
|
|
9
|
+
import { useSnackBar } from '../../providers/snackBar';
|
|
10
|
+
import CloseIcon from '@mui/icons-material/Close';
|
|
11
|
+
import { Z_INDEX } from '../../../theme/custom';
|
|
12
|
+
const getTransformForMessageCount = (messageCount) => messageCount ? `translateY(-${messageCount * 4 + 1}rem)` : '0rem';
|
|
13
|
+
const getAnimation = (messageCount, previousMessageCount) => {
|
|
14
|
+
return keyframes `
|
|
15
|
+
from {
|
|
16
|
+
transform: ${getTransformForMessageCount(previousMessageCount)};
|
|
17
|
+
}
|
|
18
|
+
to {
|
|
19
|
+
transform: ${getTransformForMessageCount(messageCount)};
|
|
20
|
+
}
|
|
21
|
+
`;
|
|
22
|
+
};
|
|
23
|
+
export const SnackBar = () => {
|
|
24
|
+
const { messages, removeMessage, previousMessageCount } = useSnackBar();
|
|
25
|
+
const typographyStyles = getTypographyStyles(TYPOGRAPHY_TYPE.CONDENSED_TEXT_MEDIUM);
|
|
26
|
+
const animation = getAnimation(messages.length, previousMessageCount);
|
|
27
|
+
const animationStyle = `${animation} 100ms linear 1 forwards`;
|
|
28
|
+
return (_jsx(Column, Object.assign({ sx: {
|
|
29
|
+
position: 'fixed',
|
|
30
|
+
left: 0,
|
|
31
|
+
top: '100%',
|
|
32
|
+
width: '100vw',
|
|
33
|
+
display: 'flex',
|
|
34
|
+
alignItems: 'center',
|
|
35
|
+
pb: 3,
|
|
36
|
+
animation: animationStyle,
|
|
37
|
+
zIndex: Z_INDEX.DIALOG,
|
|
38
|
+
} }, { children: messages.map((message) => (_jsxs(Row, Object.assign({ sx: Object.assign({ width: ['90vw', '500px'], height: '56px', borderRadius: '4px', p: 4, mt: '0.5rem', border: '1px solid', bg: COLOR.PRIMARY, color: COLOR.WHITE, justifyContent: 'space-between', alignItems: 'center' }, typographyStyles) }, { children: [_jsx(Box, Object.assign({ sx: {
|
|
39
|
+
overflow: 'hidden',
|
|
40
|
+
whiteSpace: 'nowrap',
|
|
41
|
+
textOverflow: 'ellipsis',
|
|
42
|
+
}, "aria-live": "polite" }, { children: message.message })), _jsx(Box, { children: _jsx(Button, { "aria-label": "Dismiss Notice", type: BUTTON_TYPE.TEXT, primaryIcon: CloseIcon, onClick: () => {
|
|
43
|
+
removeMessage(message.id);
|
|
44
|
+
}, textColor: COLOR.WHITE }) })] }), message.id))) })));
|
|
45
|
+
};
|
|
@@ -8,11 +8,11 @@ import { INPUT_SIZE, labelOffsetMap, labelTypographyMap } from '../TextInput';
|
|
|
8
8
|
import { ListBox } from '../../ListBox';
|
|
9
9
|
import { BUTTON_SIZE, BUTTON_TYPE, Button } from '../../Button';
|
|
10
10
|
import { defaultRenderOptionLabel, getOptionId, } from '../option';
|
|
11
|
-
import { useUniqueHtmlId } from '../../../../utils/hooks/useUniqueHtmlId';
|
|
12
11
|
import { KEY_CODES, importedDefaultComponentShim } from '../../../../utils/misc';
|
|
13
12
|
import { COLOR } from '../../../../theme/colors';
|
|
14
13
|
import { Box } from '../../layout/Box';
|
|
15
14
|
import { Label } from '../../text/Label';
|
|
15
|
+
import { useUniqueId } from '../../../providers/uniqueIds';
|
|
16
16
|
const ArrowDropDownIcon = importedDefaultComponentShim(_ArrowDropDownIcon);
|
|
17
17
|
const typographyMap = {
|
|
18
18
|
[INPUT_SIZE.SM]: TYPOGRAPHY_TYPE.PARAGRAPH_SMALL,
|
|
@@ -32,8 +32,8 @@ const buttonSizeMap = {
|
|
|
32
32
|
const DEFAULT_WIDTH = '16rem';
|
|
33
33
|
export function Select({ size: sizeParam, placeholder, leftIcon, value, label, onSelectOption, options, renderOption, inverted, renderOptionLabel: renderOptionLabelParam, sx, }) {
|
|
34
34
|
const theme = useTheme();
|
|
35
|
-
const listboxId =
|
|
36
|
-
const inputId =
|
|
35
|
+
const listboxId = useUniqueId('select-list-box');
|
|
36
|
+
const inputId = useUniqueId('select-input');
|
|
37
37
|
const [isOpen, setIsOpen] = useState(false);
|
|
38
38
|
const [stagedOptionValue, setStagedOptionValue] = useState();
|
|
39
39
|
const currentOption = useMemo(() => {
|
|
@@ -132,7 +132,7 @@ export function Select({ size: sizeParam, placeholder, leftIcon, value, label, o
|
|
|
132
132
|
overflow: 'hidden',
|
|
133
133
|
minWidth: '0%',
|
|
134
134
|
borderColor: inverted ? COLOR.GRAY : COLOR.LIGHT_GRAY,
|
|
135
|
-
zIndex: theme.zIndex.
|
|
135
|
+
zIndex: theme.zIndex.ELEVATED,
|
|
136
136
|
} }, { children: _jsx("legend", Object.assign({ style: {
|
|
137
137
|
width: 'auto',
|
|
138
138
|
float: 'unset',
|
|
@@ -154,7 +154,7 @@ export function Select({ size: sizeParam, placeholder, leftIcon, value, label, o
|
|
|
154
154
|
position: 'absolute',
|
|
155
155
|
left: '0.5rem',
|
|
156
156
|
lineHeight: 1,
|
|
157
|
-
zIndex: theme.zIndex.
|
|
157
|
+
zIndex: theme.zIndex.ELEVATED,
|
|
158
158
|
color: inverted ? COLOR.EXTRA_LIGHT_GRAY : COLOR.PRIMARY,
|
|
159
159
|
px: 1,
|
|
160
160
|
top: labelOffsetMap[size],
|
|
@@ -171,7 +171,7 @@ export function Select({ size: sizeParam, placeholder, leftIcon, value, label, o
|
|
|
171
171
|
onSelectOption && onSelectOption(option.value);
|
|
172
172
|
close();
|
|
173
173
|
}, ref: refs.setFloating, renderOption: renderOption, sx: {
|
|
174
|
-
zIndex: theme.zIndex.
|
|
174
|
+
zIndex: theme.zIndex.DIALOG,
|
|
175
175
|
}, style: Object.assign(Object.assign({ minWidth: dropdownMinWidth }, floatingStyles), { boxShadow: inverted
|
|
176
176
|
? theme.boxShadow.INVERTED
|
|
177
177
|
: theme.boxShadow.NORMAL }) }))] })));
|
|
@@ -16,7 +16,7 @@ import { useTheme } from '../../../../theme';
|
|
|
16
16
|
import { Icon } from '../../Icon';
|
|
17
17
|
import { COLOR } from '../../../../theme/colors';
|
|
18
18
|
import { Label } from '../../text/Label';
|
|
19
|
-
import {
|
|
19
|
+
import { useUniqueId } from '../../../providers/uniqueIds';
|
|
20
20
|
export var INPUT_SIZE;
|
|
21
21
|
(function (INPUT_SIZE) {
|
|
22
22
|
INPUT_SIZE["SM"] = "sm";
|
|
@@ -50,7 +50,7 @@ const defaultHeight = {
|
|
|
50
50
|
};
|
|
51
51
|
export const TextInput = React.forwardRef((_a, ref) => {
|
|
52
52
|
var { value, label, onChange, onChangeRaw, onClick, inline, inverted, size: sizeParam, leftIcon, sx, disabled } = _a, rest = __rest(_a, ["value", "label", "onChange", "onChangeRaw", "onClick", "inline", "inverted", "size", "leftIcon", "sx", "disabled"]);
|
|
53
|
-
const id =
|
|
53
|
+
const id = useUniqueId('text-input');
|
|
54
54
|
const theme = useTheme();
|
|
55
55
|
const size = sizeParam || INPUT_SIZE.MD;
|
|
56
56
|
const display = inline === false ? 'flex' : 'inline-flex';
|
|
@@ -79,7 +79,7 @@ export const TextInput = React.forwardRef((_a, ref) => {
|
|
|
79
79
|
overflow: 'hidden',
|
|
80
80
|
minWidth: '0%',
|
|
81
81
|
borderColor: inverted ? COLOR.GRAY : COLOR.LIGHT_GRAY,
|
|
82
|
-
zIndex: theme.zIndex.
|
|
82
|
+
zIndex: theme.zIndex.ELEVATED,
|
|
83
83
|
}, "aria-ignore": true }, { children: _jsx("legend", Object.assign({ style: {
|
|
84
84
|
width: 'auto',
|
|
85
85
|
float: 'unset',
|
|
@@ -112,7 +112,7 @@ export const TextInput = React.forwardRef((_a, ref) => {
|
|
|
112
112
|
color: inverted ? COLOR.EXTRA_LIGHT_GRAY : COLOR.ND_PROVOST_BLUE,
|
|
113
113
|
left: '0.5rem',
|
|
114
114
|
lineHeight: 1,
|
|
115
|
-
zIndex: theme.zIndex.
|
|
115
|
+
zIndex: theme.zIndex.ELEVATED,
|
|
116
116
|
px: 1,
|
|
117
117
|
top: labelOffsetMap[size],
|
|
118
118
|
} }, { children: label })))] })));
|
|
@@ -13,7 +13,7 @@ type ListBoxProps<Value extends Key, Option extends BasicOption<Value>> = Styled
|
|
|
13
13
|
onBlur?: () => void;
|
|
14
14
|
}>;
|
|
15
15
|
export declare const ListBox: <Value extends React.Key = string, Option extends BasicOption<Value> = any>(props: {
|
|
16
|
-
sx?: import("
|
|
16
|
+
sx?: import("../../..").StylesProp | undefined;
|
|
17
17
|
children?: React.ReactNode;
|
|
18
18
|
} & Omit<React.HTMLAttributes<HTMLDivElement>, "children" | "onChange"> & {
|
|
19
19
|
options: Option[];
|
|
@@ -12,7 +12,7 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
12
12
|
import { jsx as _jsx } from "theme-ui/jsx-runtime";
|
|
13
13
|
import { TYPOGRAPHY_TYPE, getTypographyStyles } from '../../../theme/typography';
|
|
14
14
|
import { COLOR } from '../../../theme/colors';
|
|
15
|
-
import {
|
|
15
|
+
import { useUniqueId } from '../../providers/uniqueIds';
|
|
16
16
|
export const TabList = (_a) => {
|
|
17
17
|
var { children, sx } = _a, props = __rest(_a, ["children", "sx"]);
|
|
18
18
|
return (_jsx("div", Object.assign({ role: "tablist", sx: Object.assign({ flexDirection: 'row' }, sx) }, props, { children: children })));
|
|
@@ -20,7 +20,7 @@ export const TabList = (_a) => {
|
|
|
20
20
|
export const Tab = (_a) => {
|
|
21
21
|
var { selected, children } = _a, rest = __rest(_a, ["selected", "children"]);
|
|
22
22
|
const typographyStyles = getTypographyStyles(TYPOGRAPHY_TYPE.CONDENSED_TEXT_LARGE);
|
|
23
|
-
const labelId =
|
|
23
|
+
const labelId = useUniqueId('tab-label');
|
|
24
24
|
return (_jsx("button", Object.assign({ role: "tab", sx: Object.assign({ height: '3.25rem', boxSizing: 'border-box', px: 4, bg: COLOR.WHITE, color: selected ? COLOR.DARK_GRAY : COLOR.GRAY, border: 'none', borderBottom: selected ? 'solid 3px' : 'solid 1px', borderColor: selected ? COLOR.ND_BLUE_BRIGHT : COLOR.LIGHT_GRAY, cursor: 'pointer' }, typographyStyles), "aria-labelledby": labelId, "aria-selected": selected ? 'true' : 'false' }, rest, { children: _jsx("div", Object.assign({ id: labelId, sx: {
|
|
25
25
|
transform: selected ? 'scale(1.1)' : undefined,
|
|
26
26
|
':hover': {
|
|
@@ -2,8 +2,8 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import React, { useEffect, useState } from 'react';
|
|
3
3
|
import { useEnvironment } from './env';
|
|
4
4
|
import { KEY_CODES, equals } from '../../utils/misc';
|
|
5
|
-
import { useUniqueHtmlId } from '../../utils/hooks/useUniqueHtmlId';
|
|
6
5
|
import { useCheckMidClick } from '../../utils/hooks/useCheckMidClick';
|
|
6
|
+
import { useUniqueId } from './uniqueIds';
|
|
7
7
|
export var MENU_ACTION_TYPE;
|
|
8
8
|
(function (MENU_ACTION_TYPE) {
|
|
9
9
|
MENU_ACTION_TYPE["SUBMENU"] = "SUBMENU";
|
|
@@ -41,14 +41,14 @@ const inactiveStyles = {
|
|
|
41
41
|
};
|
|
42
42
|
export const MenuProvider = ({ menu, children, }) => {
|
|
43
43
|
const { flagInDevelopment } = useEnvironment();
|
|
44
|
-
const
|
|
44
|
+
const menuId = useUniqueId('hcl-menu');
|
|
45
45
|
const { isMidClick, elementProps: checkMidclickProps } = useCheckMidClick();
|
|
46
46
|
const [usingKeyboard, setUsingKeyboard] = useState(false);
|
|
47
47
|
const closeOnBlurTimeout = React.useRef();
|
|
48
48
|
const initialPath = React.useMemo(() => [menu.items[0].id], [menu]);
|
|
49
49
|
const [activePath, setActivePath] = useState(initialPath);
|
|
50
50
|
const [isOpen, setIsOpen] = useState(false);
|
|
51
|
-
const getItemId = React.useCallback((path) =>
|
|
51
|
+
const getItemId = React.useCallback((path) => `${menuId}-item-${path.join('-')}`, [menuId]);
|
|
52
52
|
useEffect(() => {
|
|
53
53
|
if (activePath && isOpen) {
|
|
54
54
|
const activeChild = window.document.getElementById(getItemId(activePath));
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
type SnackBarMessage = {
|
|
3
|
+
message: string;
|
|
4
|
+
content?: JSX.Element;
|
|
5
|
+
id?: string;
|
|
6
|
+
};
|
|
7
|
+
type SendMessageOptions = {
|
|
8
|
+
timeout?: number;
|
|
9
|
+
wipePrevious?: boolean;
|
|
10
|
+
};
|
|
11
|
+
type SnackBarMessageWithId = Omit<SnackBarMessage, 'id'> & {
|
|
12
|
+
id: string;
|
|
13
|
+
};
|
|
14
|
+
type AddMessage = (message: SnackBarMessage, options?: SendMessageOptions) => void;
|
|
15
|
+
type SnackBarContextType = {
|
|
16
|
+
addMessage: AddMessage;
|
|
17
|
+
removeMessage: (id: string) => void;
|
|
18
|
+
messages: SnackBarMessageWithId[];
|
|
19
|
+
previousMessageCount: number;
|
|
20
|
+
};
|
|
21
|
+
export declare const SnackBarProvider: React.FC<React.PropsWithChildren>;
|
|
22
|
+
export declare const useSnackBar: () => SnackBarContextType;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useCallback, useContext, useEffect, useRef, useState, } from 'react';
|
|
3
|
+
const DEFAULT_TIMEOUT = 10000;
|
|
4
|
+
const SnackBarContext = createContext({
|
|
5
|
+
addMessage: () => { },
|
|
6
|
+
removeMessage: () => { },
|
|
7
|
+
previousMessageCount: 0,
|
|
8
|
+
messages: [],
|
|
9
|
+
});
|
|
10
|
+
export const SnackBarProvider = ({ children, }) => {
|
|
11
|
+
const [messages, setMessages] = useState([]);
|
|
12
|
+
const removeMessageTimeouts = useRef([]);
|
|
13
|
+
const previousMessageCount = useRef(0);
|
|
14
|
+
const removeMessage = useCallback((id) => {
|
|
15
|
+
setMessages((messages) => {
|
|
16
|
+
previousMessageCount.current = messages.length;
|
|
17
|
+
return messages.filter((m) => m.id !== id);
|
|
18
|
+
});
|
|
19
|
+
}, []);
|
|
20
|
+
const addMessage = useCallback((message, options = {}) => {
|
|
21
|
+
const newRandomId = Math.random().toString(36).substring(7);
|
|
22
|
+
setMessages((messages) => {
|
|
23
|
+
previousMessageCount.current = options.wipePrevious
|
|
24
|
+
? 0
|
|
25
|
+
: messages.length;
|
|
26
|
+
let newMessages = options.wipePrevious ? [] : [...messages];
|
|
27
|
+
newMessages = newMessages.filter((m) => m.id !== message.id);
|
|
28
|
+
newMessages.push(Object.assign(Object.assign({}, message), { id: message.id || newRandomId }));
|
|
29
|
+
if (newMessages.length > 3) {
|
|
30
|
+
newMessages.shift();
|
|
31
|
+
}
|
|
32
|
+
return newMessages;
|
|
33
|
+
});
|
|
34
|
+
const removeMessageTimeout = setTimeout(() => {
|
|
35
|
+
removeMessage(newRandomId);
|
|
36
|
+
}, options.timeout || DEFAULT_TIMEOUT);
|
|
37
|
+
removeMessageTimeouts.current.push(removeMessageTimeout);
|
|
38
|
+
}, [removeMessage]);
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
return () => {
|
|
41
|
+
removeMessageTimeouts.current.forEach((timeout) => clearTimeout(timeout));
|
|
42
|
+
};
|
|
43
|
+
}, []);
|
|
44
|
+
return (_jsx(SnackBarContext.Provider, Object.assign({ value: {
|
|
45
|
+
messages,
|
|
46
|
+
removeMessage,
|
|
47
|
+
previousMessageCount: previousMessageCount.current,
|
|
48
|
+
addMessage,
|
|
49
|
+
} }, { children: children })));
|
|
50
|
+
};
|
|
51
|
+
export const useSnackBar = () => {
|
|
52
|
+
return useContext(SnackBarContext);
|
|
53
|
+
};
|
|
@@ -7,7 +7,9 @@ import { FontLoader } from '../../FontLoader';
|
|
|
7
7
|
import { GlobalStyles } from '../../theme/GlobalStyles';
|
|
8
8
|
import { DialogsProvider } from './dialogs';
|
|
9
9
|
import { MediaSizeProvider } from './media';
|
|
10
|
+
import { SnackBarProvider } from './snackBar';
|
|
11
|
+
import { UniqueIdProvider } from './uniqueIds';
|
|
10
12
|
export const UiProvider = ({ env, components, alertsConfig, children, includeTheme = true, loadFonts, loadGlobalStyles, }) => {
|
|
11
|
-
const core = (_jsxs(EnvironmentProvider, Object.assign({ env: env }, { children: [_jsx(MediaSizeProvider, { children: _jsx(ComponentConfigProvider, Object.assign({ config: components || {} }, { children: _jsx(DialogsProvider, { children: _jsx(AlertsProvider, Object.assign({}, alertsConfig, { children: children })) }) })) }), loadGlobalStyles && _jsx(GlobalStyles, {}), loadFonts && _jsx(FontLoader, {})] })));
|
|
13
|
+
const core = (_jsxs(EnvironmentProvider, Object.assign({ env: env }, { children: [_jsx(MediaSizeProvider, { children: _jsx(ComponentConfigProvider, Object.assign({ config: components || {} }, { children: _jsx(UniqueIdProvider, { children: _jsx(SnackBarProvider, { children: _jsx(DialogsProvider, { children: _jsx(AlertsProvider, Object.assign({}, alertsConfig, { children: children })) }) }) }) })) }), loadGlobalStyles && _jsx(GlobalStyles, {}), loadFonts && _jsx(FontLoader, {})] })));
|
|
12
14
|
return includeTheme ? _jsx(ThemeProvider, { children: core }) : core;
|
|
13
15
|
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useContext, useMemo, useRef } from 'react';
|
|
3
|
+
const UniqueIdContext = createContext({});
|
|
4
|
+
export const UniqueIdProvider = ({ children, }) => {
|
|
5
|
+
const countIndexRef = useRef({});
|
|
6
|
+
return (_jsx(UniqueIdContext.Provider, Object.assign({ value: countIndexRef.current }, { children: children })));
|
|
7
|
+
};
|
|
8
|
+
export const useUniqueId = (prefix) => {
|
|
9
|
+
const countIndex = useContext(UniqueIdContext);
|
|
10
|
+
return useMemo(() => {
|
|
11
|
+
var _a;
|
|
12
|
+
const idForPrefix = ((_a = countIndex[prefix]) === null || _a === void 0 ? void 0 : _a.count) || 1;
|
|
13
|
+
if (!countIndex[prefix]) {
|
|
14
|
+
countIndex[prefix] = {
|
|
15
|
+
count: 2,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
countIndex[prefix].count += 1;
|
|
20
|
+
}
|
|
21
|
+
return `${prefix}-${idForPrefix}`;
|
|
22
|
+
}, []);
|
|
23
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { render } from '@testing-library/react';
|
|
3
|
+
import { UniqueIdProvider, useUniqueId } from './uniqueIds';
|
|
4
|
+
const ExampleInner = () => {
|
|
5
|
+
const uniqueId1 = useUniqueId('test-1');
|
|
6
|
+
const uniqueId2 = useUniqueId('test-1');
|
|
7
|
+
const uniqueId3 = useUniqueId('test-2');
|
|
8
|
+
return (_jsxs(_Fragment, { children: [_jsx("div", { children: uniqueId1 }), _jsx("div", { children: uniqueId2 }), _jsx("div", { children: uniqueId3 })] }));
|
|
9
|
+
};
|
|
10
|
+
const ExampleOuter = () => {
|
|
11
|
+
return (_jsxs(UniqueIdProvider, { children: [_jsx(ExampleInner, {}), _jsx(ExampleInner, {}), _jsx(ExampleInner, {})] }));
|
|
12
|
+
};
|
|
13
|
+
describe('UniqueIdProvider', () => {
|
|
14
|
+
it('should generate unique ids', () => {
|
|
15
|
+
const { getByText } = render(_jsx(ExampleOuter, {}));
|
|
16
|
+
expect(getByText('test-1-1')).toBeInTheDocument();
|
|
17
|
+
expect(getByText('test-1-2')).toBeInTheDocument();
|
|
18
|
+
expect(getByText('test-2-1')).toBeInTheDocument();
|
|
19
|
+
expect(getByText('test-1-3')).toBeInTheDocument();
|
|
20
|
+
expect(getByText('test-1-4')).toBeInTheDocument();
|
|
21
|
+
expect(getByText('test-2-2')).toBeInTheDocument();
|
|
22
|
+
expect(getByText('test-1-5')).toBeInTheDocument();
|
|
23
|
+
expect(getByText('test-1-6')).toBeInTheDocument();
|
|
24
|
+
expect(getByText('test-2-3')).toBeInTheDocument();
|
|
25
|
+
});
|
|
26
|
+
});
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { theme, ThemeProvider, useTheme } from './theme';
|
|
2
|
+
export type { StylesProp } from './theme';
|
|
2
3
|
export { COLOR } from './theme/colors';
|
|
3
4
|
export { TYPOGRAPHY_TYPE, FONT, FONT_SIZE, FONT_WEIGHT, LINE_HEIGHT, } from './theme/typography';
|
|
4
5
|
export { GlobalStyles } from './theme/GlobalStyles';
|
|
@@ -31,6 +32,7 @@ export { Pill, PILL_SIZE, PILL_TYPE } from './components/elements/Pill';
|
|
|
31
32
|
export { Table, TableColumn } from './components/elements/Table';
|
|
32
33
|
export { TabList, Tab } from './components/elements/TabList';
|
|
33
34
|
export { Card, CARD_SIZE, CARD_LAYOUT } from './components/composites/Card';
|
|
35
|
+
export { SnackBar } from './components/composites/SnackBar';
|
|
34
36
|
export { NavMenu } from './components/composites/NavMenu';
|
|
35
37
|
export { EmptyState, EMPTY_STATE_SIZE, } from './components/composites/EmptyState';
|
|
36
38
|
export { DropdownLinks } from './components/composites/DropdownLinks';
|
|
@@ -39,7 +41,9 @@ export { DragDropList, DragHandle } from './components/composites/DragDropList';
|
|
|
39
41
|
export { Seo } from './components/composites/Seo';
|
|
40
42
|
export { UiProvider } from './components/providers/ui';
|
|
41
43
|
export { MenuProvider, useMenu } from './components/providers/menu';
|
|
44
|
+
export { SnackBarProvider, useSnackBar } from './components/providers/snackBar';
|
|
42
45
|
export { useAlerts, AlertsProvider, ALERT_DOMAIN, } from './components/providers/alerts';
|
|
43
46
|
export { MediaSizeProvider, useMediaQuery } from './components/providers/media';
|
|
44
47
|
export { DialogsProvider, useDialog } from './components/providers/dialogs';
|
|
48
|
+
export { useUniqueId } from './components/providers/uniqueIds';
|
|
45
49
|
export { useHover } from './utils/hooks/useHover';
|
package/dist/index.js
CHANGED
|
@@ -31,6 +31,7 @@ export { Pill, PILL_SIZE, PILL_TYPE } from './components/elements/Pill';
|
|
|
31
31
|
export { Table, TableColumn } from './components/elements/Table';
|
|
32
32
|
export { TabList, Tab } from './components/elements/TabList';
|
|
33
33
|
export { Card, CARD_SIZE, CARD_LAYOUT } from './components/composites/Card';
|
|
34
|
+
export { SnackBar } from './components/composites/SnackBar';
|
|
34
35
|
export { NavMenu } from './components/composites/NavMenu';
|
|
35
36
|
export { EmptyState, EMPTY_STATE_SIZE, } from './components/composites/EmptyState';
|
|
36
37
|
export { DropdownLinks } from './components/composites/DropdownLinks';
|
|
@@ -39,7 +40,9 @@ export { DragDropList, DragHandle } from './components/composites/DragDropList';
|
|
|
39
40
|
export { Seo } from './components/composites/Seo';
|
|
40
41
|
export { UiProvider } from './components/providers/ui';
|
|
41
42
|
export { MenuProvider, useMenu } from './components/providers/menu';
|
|
43
|
+
export { SnackBarProvider, useSnackBar } from './components/providers/snackBar';
|
|
42
44
|
export { useAlerts, AlertsProvider, ALERT_DOMAIN, } from './components/providers/alerts';
|
|
43
45
|
export { MediaSizeProvider, useMediaQuery } from './components/providers/media';
|
|
44
46
|
export { DialogsProvider, useDialog } from './components/providers/dialogs';
|
|
47
|
+
export { useUniqueId } from './components/providers/uniqueIds';
|
|
45
48
|
export { useHover } from './utils/hooks/useHover';
|
package/dist/theme/custom.d.ts
CHANGED
package/dist/theme/custom.js
CHANGED
|
@@ -7,7 +7,7 @@ export var BOX_SHADOW;
|
|
|
7
7
|
export var Z_INDEX;
|
|
8
8
|
(function (Z_INDEX) {
|
|
9
9
|
Z_INDEX[Z_INDEX["NORMAL"] = 1] = "NORMAL";
|
|
10
|
-
Z_INDEX[Z_INDEX["
|
|
11
|
-
Z_INDEX[Z_INDEX["
|
|
10
|
+
Z_INDEX[Z_INDEX["ELEVATED"] = 2] = "ELEVATED";
|
|
11
|
+
Z_INDEX[Z_INDEX["DIALOG"] = 3] = "DIALOG";
|
|
12
12
|
Z_INDEX[Z_INDEX["MODAL"] = 4] = "MODAL";
|
|
13
13
|
})(Z_INDEX || (Z_INDEX = {}));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ndlib/component-library",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.52",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"files": [
|
|
@@ -44,7 +44,6 @@
|
|
|
44
44
|
"@emotion/styled": "^11.11.0",
|
|
45
45
|
"@mui/icons-material": "^5.11.16",
|
|
46
46
|
"@mui/material": "^5.11.16",
|
|
47
|
-
"@types/pretty": "^2.0.1",
|
|
48
47
|
"@storybook/addon-essentials": "^7.0.17",
|
|
49
48
|
"@storybook/addon-interactions": "^7.0.18",
|
|
50
49
|
"@storybook/addon-links": "^7.0.17",
|
|
@@ -54,6 +53,7 @@
|
|
|
54
53
|
"@testing-library/jest-dom": "^5.16.5",
|
|
55
54
|
"@testing-library/react": "^14.0.0",
|
|
56
55
|
"@testing-library/user-event": "^14.4.3",
|
|
56
|
+
"@types/pretty": "^2.0.1",
|
|
57
57
|
"@types/react": "^18.0.28",
|
|
58
58
|
"@types/react-beautiful-dnd": "^13.1.5",
|
|
59
59
|
"@types/react-datepicker": "^4.15.0",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const useUniqueHtmlId: (prefix?: string) => string;
|