@kaizen/components 3.1.6 → 3.3.0
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/cjs/src/Avatar/Avatar.cjs +18 -14
- package/dist/cjs/src/DateInput/DateInput/DateInput.cjs +1 -2
- package/dist/cjs/src/DatePicker/DatePicker.cjs +6 -4
- package/dist/cjs/src/Focusable/Focusable.cjs +5 -4
- package/dist/cjs/src/MultiSelect/MultiSelect.cjs +4 -2
- package/dist/cjs/src/RichTextEditor/RichTextEditor/RichTextEditor.cjs +4 -1
- package/dist/cjs/src/RichTextEditor/RichTextEditor/utils/inputrules.cjs +0 -1
- package/dist/cjs/src/RichTextEditor/utils/core/createRichTextEditor.cjs +2 -1
- package/dist/cjs/src/RichTextEditor/utils/core/hooks/useRichTextEditor.cjs +24 -9
- package/dist/cjs/src/SingleSelect/SingleSelect.cjs +4 -2
- package/dist/cjs/src/Tabs/subcomponents/TabList/TabList.cjs +8 -1
- package/dist/cjs/src/TimeField/TimeField.cjs +9 -4
- package/dist/cjs/src/TimeField/subcomponents/TimeSegment/TimeSegment.cjs +6 -4
- package/dist/esm/src/Avatar/Avatar.mjs +18 -15
- package/dist/esm/src/DateInput/DateInput/DateInput.mjs +1 -2
- package/dist/esm/src/DatePicker/DatePicker.mjs +6 -4
- package/dist/esm/src/Focusable/Focusable.mjs +5 -4
- package/dist/esm/src/MultiSelect/MultiSelect.mjs +4 -2
- package/dist/esm/src/RichTextEditor/RichTextEditor/RichTextEditor.mjs +4 -1
- package/dist/esm/src/RichTextEditor/RichTextEditor/utils/inputrules.mjs +0 -1
- package/dist/esm/src/RichTextEditor/utils/core/createRichTextEditor.mjs +2 -1
- package/dist/esm/src/RichTextEditor/utils/core/hooks/useRichTextEditor.mjs +24 -9
- package/dist/esm/src/SingleSelect/SingleSelect.mjs +4 -2
- package/dist/esm/src/Tabs/subcomponents/TabList/TabList.mjs +8 -1
- package/dist/esm/src/TimeField/TimeField.mjs +9 -4
- package/dist/esm/src/TimeField/subcomponents/TimeSegment/TimeSegment.mjs +6 -4
- package/dist/styles.css +15 -15
- package/dist/types/DateInput/DateInputWithIconButton/DateInputWithIconButton.d.ts +2 -2
- package/dist/types/DatePicker/DatePicker.d.ts +3 -2
- package/dist/types/Focusable/Focusable.d.ts +8 -3
- package/dist/types/Input/Input/Input.d.ts +1 -1
- package/dist/types/MultiSelect/MultiSelect.d.ts +3 -2
- package/dist/types/Notification/index.d.ts +1 -0
- package/dist/types/RichTextEditor/RichTextEditor/RichTextEditor.d.ts +2 -1
- package/dist/types/RichTextEditor/utils/core/createRichTextEditor.d.ts +1 -0
- package/dist/types/RichTextEditor/utils/core/hooks/useRichTextEditor.d.ts +3 -2
- package/dist/types/SingleSelect/SingleSelect.d.ts +3 -3
- package/dist/types/TextArea/TextArea.d.ts +1 -1
- package/dist/types/TimeField/TimeField.d.ts +1 -0
- package/dist/types/TimeField/subcomponents/TimeSegment/TimeSegment.d.ts +3 -1
- package/package.json +7 -7
- package/src/Avatar/Avatar.tsx +19 -11
- package/src/DateInput/DateInput/DateInput.tsx +1 -2
- package/src/DateInput/DateInputWithIconButton/DateInputWithIconButton.tsx +2 -2
- package/src/DatePicker/DatePicker.tsx +6 -3
- package/src/Focusable/Focusable.tsx +17 -7
- package/src/Input/Input/Input.tsx +1 -1
- package/src/MultiSelect/MultiSelect.tsx +4 -1
- package/src/Notification/index.ts +1 -0
- package/src/RichTextEditor/RichTextEditor/RichTextEditor.tsx +3 -0
- package/src/RichTextEditor/utils/core/createRichTextEditor.spec.ts +1 -1
- package/src/RichTextEditor/utils/core/createRichTextEditor.ts +2 -0
- package/src/RichTextEditor/utils/core/hooks/useRichTextEditor.spec.tsx +8 -3
- package/src/RichTextEditor/utils/core/hooks/useRichTextEditor.ts +20 -7
- package/src/SingleSelect/SingleSelect.tsx +5 -3
- package/src/Tabs/_docs/Tabs.spec.stories.tsx +39 -0
- package/src/Tabs/_docs/Tabs.stories.tsx +38 -2
- package/src/Tabs/subcomponents/TabList/TabList.tsx +9 -1
- package/src/TextArea/TextArea.tsx +1 -1
- package/src/TimeField/TimeField.tsx +17 -15
- package/src/TimeField/subcomponents/TimeSegment/TimeSegment.tsx +6 -3
|
@@ -7,8 +7,8 @@ export type DateInputWithIconButtonProps = {
|
|
|
7
7
|
onButtonClick: React.MouseEventHandler<HTMLButtonElement>;
|
|
8
8
|
} & Omit<DateInputProps, 'startIconAdornment' | 'endIconAdornment'>;
|
|
9
9
|
export type DateInputWithIconButtonRefs = {
|
|
10
|
-
inputRef?: React.
|
|
11
|
-
buttonRef?: React.
|
|
10
|
+
inputRef?: React.Ref<HTMLInputElement>;
|
|
11
|
+
buttonRef?: React.Ref<HTMLButtonElement>;
|
|
12
12
|
};
|
|
13
13
|
export declare const DateInputWithIconButton: React.ForwardRefExoticComponent<{
|
|
14
14
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type RefObject } from 'react';
|
|
1
|
+
import React, { type RefObject } from 'react';
|
|
2
2
|
import { type CalendarSingleProps, type DisabledDayMatchers } from "../Calendar";
|
|
3
3
|
import { type DateInputFieldProps } from './subcomponents/DateInputField';
|
|
4
4
|
import type { ValidationResponse } from './types';
|
|
@@ -6,6 +6,7 @@ import { type DatePickerSupportedLocales } from './utils/getLocale';
|
|
|
6
6
|
type OmittedDateInputFieldProps = 'onClick' | 'onFocus' | 'onChange' | 'onBlur' | 'onButtonClick' | 'value' | 'locale' | 'id';
|
|
7
7
|
export type DatePickerProps = {
|
|
8
8
|
id?: string;
|
|
9
|
+
inputRef?: React.Ref<HTMLInputElement>;
|
|
9
10
|
buttonRef?: RefObject<HTMLButtonElement>;
|
|
10
11
|
onInputClick?: DateInputFieldProps['onClick'];
|
|
11
12
|
onInputFocus?: DateInputFieldProps['onFocus'];
|
|
@@ -49,7 +50,7 @@ export type DatePickerProps = {
|
|
|
49
50
|
* {@link https://cultureamp.design/?path=/docs/components-date-controls-datepicker--docs Storybook}
|
|
50
51
|
*/
|
|
51
52
|
export declare const DatePicker: {
|
|
52
|
-
({ id: propsId, buttonRef: propsButtonRef, locale: propsLocale, disabledDates, disabledDaysOfWeek, disabledRange, disabledBeforeAfter, disabledBefore, disabledAfter, weekStartsOn, defaultMonth, selectedDay, status, validationMessage, onInputClick, onInputFocus, onInputChange, onInputBlur, onButtonClick, onDayChange, onValidate, ...restDateInputFieldProps }: DatePickerProps): JSX.Element;
|
|
53
|
+
({ id: propsId, inputRef: propsInputRef, buttonRef: propsButtonRef, locale: propsLocale, disabledDates, disabledDaysOfWeek, disabledRange, disabledBeforeAfter, disabledBefore, disabledAfter, weekStartsOn, defaultMonth, selectedDay, status, validationMessage, onInputClick, onInputFocus, onInputChange, onInputBlur, onButtonClick, onDayChange, onValidate, ...restDateInputFieldProps }: DatePickerProps): JSX.Element;
|
|
53
54
|
displayName: string;
|
|
54
55
|
};
|
|
55
56
|
export {};
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
import { type HTMLAttributes, type ReactNode } from 'react';
|
|
1
|
+
import { type ElementType, type HTMLAttributes, type ReactNode } from 'react';
|
|
2
2
|
import { type FocusableOptions } from 'react-aria';
|
|
3
|
-
export type FocusableProps = {
|
|
3
|
+
export type FocusableProps<T extends ElementType = 'div'> = {
|
|
4
4
|
children: ReactNode;
|
|
5
|
+
/**
|
|
6
|
+
* The HTML element to render as. Defaults to `div` to avoid an api change
|
|
7
|
+
* but should be set to 'span' if rendering within something like a 'p' tag to avoid invalid HTML.
|
|
8
|
+
*/
|
|
9
|
+
tag?: T;
|
|
5
10
|
} & FocusableOptions & HTMLAttributes<HTMLDivElement>;
|
|
6
|
-
export declare const Focusable: ({ children, className, ...props }: FocusableProps) => JSX.Element;
|
|
11
|
+
export declare const Focusable: <T extends ElementType = "div">({ children, className, tag, ...props }: FocusableProps<T>) => JSX.Element;
|
|
@@ -4,7 +4,7 @@ import { type InputStatus, type InputTypes } from './types';
|
|
|
4
4
|
export type InputType = (typeof InputTypes)[number];
|
|
5
5
|
export type InputStatusType = (typeof InputStatus)[number];
|
|
6
6
|
export type InputProps = {
|
|
7
|
-
inputRef?: React.
|
|
7
|
+
inputRef?: React.Ref<HTMLInputElement>;
|
|
8
8
|
status?: InputStatusType;
|
|
9
9
|
startIconAdornment?: React.ReactNode;
|
|
10
10
|
endIconAdornment?: React.ReactNode;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { type HTMLAttributes } from 'react';
|
|
1
|
+
import React, { type HTMLAttributes } from 'react';
|
|
2
2
|
import { type FieldMessageProps } from "../FieldMessage";
|
|
3
3
|
import { type OverrideClassName } from "../types/OverrideClassName";
|
|
4
4
|
import { type MultiSelectOptionsProps } from './subcomponents/MultiSelectOptions';
|
|
5
5
|
import { type MultiSelectOption, type ValidationMessage } from './types';
|
|
6
6
|
export type MultiSelectProps = {
|
|
7
|
+
inputRef?: React.Ref<HTMLButtonElement>;
|
|
7
8
|
label: string;
|
|
8
9
|
items: MultiSelectOptionsProps['options'];
|
|
9
10
|
selectedValues: Set<MultiSelectOption['value']>;
|
|
@@ -18,6 +19,6 @@ export type MultiSelectProps = {
|
|
|
18
19
|
validationMessage?: ValidationMessage;
|
|
19
20
|
} & OverrideClassName<HTMLAttributes<HTMLDivElement>>;
|
|
20
21
|
export declare const MultiSelect: {
|
|
21
|
-
({ id: propsId, label, items, selectedValues, description, onSelectedValuesChange, isOpen, onOpenChange, classNameOverride, validationMessage, ...restProps }: MultiSelectProps): JSX.Element;
|
|
22
|
+
({ id: propsId, label, items, selectedValues, description, onSelectedValuesChange, isOpen, onOpenChange, classNameOverride, validationMessage, inputRef, ...restProps }: MultiSelectProps): JSX.Element;
|
|
22
23
|
displayName: string;
|
|
23
24
|
};
|
|
@@ -4,6 +4,7 @@ import type { EditorContentArray, EditorRows, ToolbarItems } from '../types';
|
|
|
4
4
|
import { ProseMirrorState } from '../utils/prosemirror';
|
|
5
5
|
type BaseRichTextEditorProps = {
|
|
6
6
|
id?: string;
|
|
7
|
+
inputRef?: React.Ref<HTMLElement>;
|
|
7
8
|
onChange: (content: ProseMirrorState.EditorState) => void;
|
|
8
9
|
defaultValue: EditorContentArray;
|
|
9
10
|
controls?: ToolbarItems[];
|
|
@@ -38,7 +39,7 @@ export type RichTextEditorProps = BaseRichTextEditorProps & (WithLabelText | Wit
|
|
|
38
39
|
* {@link https://cultureamp.design/?path=/docs/components-richtexteditor--docs Storybook}
|
|
39
40
|
*/
|
|
40
41
|
export declare const RichTextEditor: {
|
|
41
|
-
({ id, onChange, defaultValue, labelText, "aria-labelledby": labelledBy, "aria-describedby": describedBy, classNameOverride, controls, rows, dataError, onDataError, validationMessage, description, status, ...restProps }: RichTextEditorProps): JSX.Element;
|
|
42
|
+
({ id, inputRef, onChange, defaultValue, labelText, "aria-labelledby": labelledBy, "aria-describedby": describedBy, classNameOverride, controls, rows, dataError, onDataError, validationMessage, description, status, ...restProps }: RichTextEditorProps): JSX.Element;
|
|
42
43
|
displayName: string;
|
|
43
44
|
};
|
|
44
45
|
export {};
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { type EditorState } from 'prosemirror-state';
|
|
2
2
|
import { type CommandOrTransaction } from '../types';
|
|
3
3
|
type RTEOptions = {
|
|
4
|
-
editable
|
|
4
|
+
editable?: boolean;
|
|
5
|
+
inputRef?: React.Ref<HTMLElement>;
|
|
5
6
|
};
|
|
6
7
|
type SetEditableStatus = (status: boolean) => void;
|
|
7
8
|
type UseRichTextEditorReturnValue = [
|
|
@@ -18,5 +19,5 @@ type UseRichTextEditorReturnValue = [
|
|
|
18
19
|
* @param {initialEditorState} ProseMirror state
|
|
19
20
|
* @returns {Array}
|
|
20
21
|
*/
|
|
21
|
-
export declare const useRichTextEditor: (initialEditorState: EditorState, attributes?: Record<string, string>,
|
|
22
|
+
export declare const useRichTextEditor: (initialEditorState: EditorState, attributes?: Record<string, string>, { editable, inputRef }?: RTEOptions) => UseRichTextEditorReturnValue;
|
|
22
23
|
export {};
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { type UseFloatingReturn } from '@floating-ui/react-dom';
|
|
3
2
|
import { type SelectProps as AriaSelectProps } from '@react-stately/select';
|
|
4
3
|
import { type Key } from '@react-types/shared';
|
|
5
4
|
import { type OverrideClassName } from "../types/OverrideClassName";
|
|
@@ -12,13 +11,14 @@ export type SingleSelectProps<Option extends SingleSelectOption = SingleSelectOp
|
|
|
12
11
|
*/
|
|
13
12
|
items: SingleSelectItem<Option>[];
|
|
14
13
|
id?: string;
|
|
14
|
+
inputRef?: React.Ref<HTMLButtonElement>;
|
|
15
15
|
/**
|
|
16
16
|
* Optional render function that allows custom rendering of the trigger button.
|
|
17
17
|
* The function receives the `selectToggleProps` and a `ref` to be applied
|
|
18
18
|
* to the button element.
|
|
19
19
|
*/
|
|
20
20
|
trigger?: (selectToggleProps: SelectToggleProps & {
|
|
21
|
-
ref:
|
|
21
|
+
ref: React.Ref<HTMLButtonElement>;
|
|
22
22
|
}) => JSX.Element;
|
|
23
23
|
/**
|
|
24
24
|
* Optional render function that allows custom rendering of the items in the dropdown.
|
|
@@ -56,7 +56,7 @@ export type SingleSelectProps<Option extends SingleSelectOption = SingleSelectOp
|
|
|
56
56
|
* {@link https://cultureamp.design/?path=/docs/components-select--docs Storybook}
|
|
57
57
|
*/
|
|
58
58
|
export declare const SingleSelect: {
|
|
59
|
-
<Option extends SingleSelectOption = SingleSelectOption>({ label, items, id: propsId, trigger, children, status, validationMessage, isReversed, isRequired, isFullWidth, classNameOverride, selectedKey, description, isDisabled, onSelectionChange, portalContainerId, ...restProps }: SingleSelectProps<Option>): JSX.Element;
|
|
59
|
+
<Option extends SingleSelectOption = SingleSelectOption>({ label, items, id: propsId, trigger, children, status, validationMessage, isReversed, isRequired, isFullWidth, classNameOverride, selectedKey, description, isDisabled, onSelectionChange, portalContainerId, inputRef, ...restProps }: SingleSelectProps<Option>): JSX.Element;
|
|
60
60
|
displayName: string;
|
|
61
61
|
Section: {
|
|
62
62
|
<Option extends SingleSelectOption = SingleSelectOption>({ section, }: import("./subcomponents").ListBoxSectionProps<Option>): JSX.Element;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { type TextareaHTMLAttributes } from 'react';
|
|
2
2
|
import { type OverrideClassName } from "../types/OverrideClassName";
|
|
3
3
|
export type TextAreaProps = {
|
|
4
|
-
textAreaRef?: React.
|
|
4
|
+
textAreaRef?: React.Ref<HTMLTextAreaElement>;
|
|
5
5
|
status?: 'default' | 'error' | 'caution';
|
|
6
6
|
/**
|
|
7
7
|
* Grows the input height as more content is added
|
|
@@ -17,6 +17,7 @@ export type TimeFieldProps = {
|
|
|
17
17
|
value: ValueType | null;
|
|
18
18
|
status?: StatusType;
|
|
19
19
|
validationMessage?: React.ReactNode;
|
|
20
|
+
inputRef?: React.Ref<HTMLSpanElement>;
|
|
20
21
|
} & OverrideClassName<Omit<TimeFieldStateOptions, OmittedTimeFieldProps>>;
|
|
21
22
|
export declare const TimeField: {
|
|
22
23
|
(props: TimeFieldProps): JSX.Element;
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
import React from 'react';
|
|
1
2
|
import { type DateFieldState, type DateSegment } from '@react-stately/datepicker';
|
|
2
3
|
export type TimeSegmentProps = {
|
|
3
4
|
segment: DateSegment;
|
|
4
5
|
state: DateFieldState;
|
|
5
6
|
hasPadding?: boolean;
|
|
7
|
+
inputRef?: React.Ref<HTMLSpanElement>;
|
|
6
8
|
};
|
|
7
9
|
export declare const TimeSegment: {
|
|
8
|
-
({ segment, state, hasPadding, }: TimeSegmentProps): JSX.Element;
|
|
10
|
+
({ segment, state, hasPadding, inputRef, }: TimeSegmentProps): JSX.Element;
|
|
9
11
|
displayName: string;
|
|
10
12
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kaizen/components",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0",
|
|
4
4
|
"description": "Kaizen component library",
|
|
5
5
|
"author": "Geoffrey Chong <geoff.chong@cultureamp.com>",
|
|
6
6
|
"homepage": "https://cultureamp.design",
|
|
@@ -99,20 +99,20 @@
|
|
|
99
99
|
"@react-stately/select": "^3.6.14",
|
|
100
100
|
"@react-types/shared": "^3.30.0",
|
|
101
101
|
"classnames": "^2.5.1",
|
|
102
|
-
"date-fns": "^4.
|
|
102
|
+
"date-fns": "^4.4.0",
|
|
103
103
|
"lodash.debounce": "^4.0.8",
|
|
104
104
|
"nanobus": "^4.5.0",
|
|
105
105
|
"prosemirror-commands": "^1.7.1",
|
|
106
106
|
"prosemirror-history": "^1.5.0",
|
|
107
107
|
"prosemirror-inputrules": "^1.5.1",
|
|
108
108
|
"prosemirror-keymap": "^1.2.3",
|
|
109
|
-
"prosemirror-model": "^1.25.
|
|
109
|
+
"prosemirror-model": "^1.25.8",
|
|
110
110
|
"prosemirror-schema-basic": "^1.2.4",
|
|
111
111
|
"prosemirror-schema-list": "^1.5.1",
|
|
112
112
|
"prosemirror-state": "^1.4.4",
|
|
113
113
|
"prosemirror-transform": "^1.12.0",
|
|
114
114
|
"prosemirror-utils": "^1.2.2",
|
|
115
|
-
"prosemirror-view": "^1.41.
|
|
115
|
+
"prosemirror-view": "^1.41.9",
|
|
116
116
|
"react-animate-height": "^3.2.4",
|
|
117
117
|
"react-aria": "^3.41.1",
|
|
118
118
|
"react-aria-components": "^1.10.1",
|
|
@@ -130,7 +130,7 @@
|
|
|
130
130
|
},
|
|
131
131
|
"devDependencies": {
|
|
132
132
|
"@cultureamp/frontend-apis": "13.3.0",
|
|
133
|
-
"@cultureamp/i18n-react-intl": "^4.1.
|
|
133
|
+
"@cultureamp/i18n-react-intl": "^4.1.5",
|
|
134
134
|
"@cultureamp/package-bundler": "^4.0.1",
|
|
135
135
|
"cssnano": "^7.1.9",
|
|
136
136
|
"@testing-library/dom": "^10.4.1",
|
|
@@ -155,14 +155,14 @@
|
|
|
155
155
|
"react-dom": "^19.2.7",
|
|
156
156
|
"react-highlight": "^0.15.0",
|
|
157
157
|
"react-intl": "^10.1.13",
|
|
158
|
-
"rollup": "^4.
|
|
158
|
+
"rollup": "^4.61.1",
|
|
159
159
|
"sass": "1.79.6",
|
|
160
160
|
"serialize-query-params": "^2.0.4",
|
|
161
161
|
"svgo": "^4.0.1",
|
|
162
162
|
"ts-patch": "^3.3.0",
|
|
163
163
|
"tslib": "^2.8.1",
|
|
164
164
|
"tsx": "^4.22.4",
|
|
165
|
-
"@kaizen/design-tokens": "11.0.
|
|
165
|
+
"@kaizen/design-tokens": "11.0.11"
|
|
166
166
|
},
|
|
167
167
|
"devDependenciesComments": {
|
|
168
168
|
"sass": "Prevent deprecation warnings introduced in 1.80 as we plan to move away from sass",
|
package/src/Avatar/Avatar.tsx
CHANGED
|
@@ -91,21 +91,29 @@ const renderInitials = (
|
|
|
91
91
|
const isLongName = initials.length > 2 && size !== 'small'
|
|
92
92
|
const renderFallback = disableInitials || initials === ''
|
|
93
93
|
|
|
94
|
-
|
|
95
|
-
<FallbackIcon alt={alt} />
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
94
|
+
if (renderFallback) {
|
|
95
|
+
return <FallbackIcon alt={alt} />
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (isLongName) {
|
|
99
|
+
return (
|
|
100
|
+
<abbr
|
|
101
|
+
className={classnames(styles.initials, styles.longName)}
|
|
102
|
+
title={alt}
|
|
101
103
|
// Ignore Chromatic diffs since the font-size calculation has shown itself to be slightly non-deterministic,
|
|
102
104
|
// causing flaky tests.
|
|
103
|
-
|
|
105
|
+
data-chromatic="ignore"
|
|
106
|
+
>
|
|
107
|
+
<Textfit mode="single" max={getMaxFontSizePixels(size)}>
|
|
104
108
|
{initials}
|
|
105
109
|
</Textfit>
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
110
|
+
</abbr>
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return (
|
|
115
|
+
<abbr className={styles.initials} title={alt}>
|
|
116
|
+
{getInitials(fullName, size === 'small')}
|
|
109
117
|
</abbr>
|
|
110
118
|
)
|
|
111
119
|
}
|
|
@@ -2,7 +2,6 @@ import React from 'react'
|
|
|
2
2
|
import classnames from 'classnames'
|
|
3
3
|
import { Input, type InputProps } from '~components/Input'
|
|
4
4
|
import { Label } from '~components/Label'
|
|
5
|
-
import { isRefObject } from '~components/utils/isRefObject'
|
|
6
5
|
import styles from './DateInput.module.css'
|
|
7
6
|
|
|
8
7
|
type OmittedInputProps = 'reversed' | 'type' | 'inputRef'
|
|
@@ -23,7 +22,7 @@ export const DateInput = React.forwardRef<HTMLInputElement, DateInputProps>(
|
|
|
23
22
|
disabled={disabled}
|
|
24
23
|
/>
|
|
25
24
|
<Input
|
|
26
|
-
inputRef={
|
|
25
|
+
inputRef={ref}
|
|
27
26
|
id={id}
|
|
28
27
|
type="text"
|
|
29
28
|
autoComplete="off"
|
|
@@ -13,8 +13,8 @@ export type DateInputWithIconButtonProps = {
|
|
|
13
13
|
} & Omit<DateInputProps, 'startIconAdornment' | 'endIconAdornment'>
|
|
14
14
|
|
|
15
15
|
export type DateInputWithIconButtonRefs = {
|
|
16
|
-
inputRef?: React.
|
|
17
|
-
buttonRef?: React.
|
|
16
|
+
inputRef?: React.Ref<HTMLInputElement>
|
|
17
|
+
buttonRef?: React.Ref<HTMLButtonElement>
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
export const DateInputWithIconButton = React.forwardRef<
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { useEffect, useId, useRef, useState, type RefObject } from 'react'
|
|
2
2
|
import { useIntl } from '@cultureamp/i18n-react-intl'
|
|
3
|
+
import { mergeRefs } from '@react-aria/utils'
|
|
3
4
|
import { type DayEventHandler } from 'react-day-picker'
|
|
4
5
|
import { FocusOn } from 'react-focus-on'
|
|
5
6
|
import {
|
|
@@ -35,6 +36,7 @@ type OmittedDateInputFieldProps =
|
|
|
35
36
|
|
|
36
37
|
export type DatePickerProps = {
|
|
37
38
|
id?: string
|
|
39
|
+
inputRef?: React.Ref<HTMLInputElement>
|
|
38
40
|
buttonRef?: RefObject<HTMLButtonElement>
|
|
39
41
|
onInputClick?: DateInputFieldProps['onClick']
|
|
40
42
|
onInputFocus?: DateInputFieldProps['onFocus']
|
|
@@ -81,6 +83,7 @@ export type DatePickerProps = {
|
|
|
81
83
|
*/
|
|
82
84
|
export const DatePicker = ({
|
|
83
85
|
id: propsId,
|
|
86
|
+
inputRef: propsInputRef,
|
|
84
87
|
buttonRef: propsButtonRef,
|
|
85
88
|
locale: propsLocale = 'en-AU',
|
|
86
89
|
disabledDates,
|
|
@@ -115,11 +118,11 @@ export const DatePicker = ({
|
|
|
115
118
|
const id = propsId ?? reactId
|
|
116
119
|
|
|
117
120
|
const containerRef = useRef<HTMLInputElement>(null)
|
|
118
|
-
const
|
|
121
|
+
const internalInputRef = useRef<HTMLInputElement>(null)
|
|
119
122
|
const fallbackButtonRef = useRef<HTMLButtonElement>(null)
|
|
120
123
|
const buttonRef = propsButtonRef ?? fallbackButtonRef
|
|
121
124
|
const dateInputRefs = useRef({
|
|
122
|
-
inputRef,
|
|
125
|
+
inputRef: mergeRefs<HTMLInputElement>(internalInputRef, propsInputRef),
|
|
123
126
|
buttonRef,
|
|
124
127
|
})
|
|
125
128
|
const [inputValue, setInputValue] = useState<string>('')
|
|
@@ -239,7 +242,7 @@ export const DatePicker = ({
|
|
|
239
242
|
|
|
240
243
|
const handleReturnFocus = (): void => {
|
|
241
244
|
if (lastTrigger === 'inputKeydown' || lastTrigger === 'inputFocus') {
|
|
242
|
-
return
|
|
245
|
+
return internalInputRef.current?.focus()
|
|
243
246
|
}
|
|
244
247
|
buttonRef.current?.focus()
|
|
245
248
|
}
|
|
@@ -1,26 +1,36 @@
|
|
|
1
|
-
import React, { useRef, type HTMLAttributes, type ReactNode } from 'react'
|
|
1
|
+
import React, { useRef, type ElementType, type HTMLAttributes, type ReactNode } from 'react'
|
|
2
2
|
import classnames from 'classnames'
|
|
3
3
|
import { useFocusable, type FocusableOptions } from 'react-aria'
|
|
4
4
|
import styles from './Focusable.module.css'
|
|
5
5
|
|
|
6
|
-
export type FocusableProps = {
|
|
6
|
+
export type FocusableProps<T extends ElementType = 'div'> = {
|
|
7
7
|
children: ReactNode
|
|
8
|
+
/**
|
|
9
|
+
* The HTML element to render as. Defaults to `div` to avoid an api change
|
|
10
|
+
* but should be set to 'span' if rendering within something like a 'p' tag to avoid invalid HTML.
|
|
11
|
+
*/
|
|
12
|
+
tag?: T
|
|
8
13
|
} & FocusableOptions &
|
|
9
14
|
HTMLAttributes<HTMLDivElement>
|
|
10
15
|
|
|
11
|
-
export const Focusable =
|
|
16
|
+
export const Focusable = <T extends ElementType = 'div'>({
|
|
17
|
+
children,
|
|
18
|
+
className,
|
|
19
|
+
tag,
|
|
20
|
+
...props
|
|
21
|
+
}: FocusableProps<T>): JSX.Element => {
|
|
12
22
|
const ref = useRef<HTMLDivElement>(null)
|
|
13
23
|
const { focusableProps } = useFocusable(props, ref)
|
|
24
|
+
const Element = tag ?? 'div'
|
|
14
25
|
|
|
15
26
|
return (
|
|
16
|
-
<
|
|
27
|
+
<Element
|
|
17
28
|
ref={ref}
|
|
18
29
|
className={classnames(styles.focusableWrapper, className)}
|
|
19
30
|
{...focusableProps}
|
|
20
31
|
data-inline-hidden-content
|
|
21
|
-
// We want the
|
|
32
|
+
// We want the element to be focusable for keyboard users,
|
|
22
33
|
// but screen readers will have the VisuallyHidden content
|
|
23
|
-
// eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
|
|
24
34
|
tabIndex={0}
|
|
25
35
|
// aria-describedby on div doesn't do anthing so we instead render the content in VisuallyHidden from tooltip
|
|
26
36
|
// but because RAC adds it as it assumes it's interactive element we remove it here
|
|
@@ -28,6 +38,6 @@ export const Focusable = ({ children, className, ...props }: FocusableProps): JS
|
|
|
28
38
|
{...props}
|
|
29
39
|
>
|
|
30
40
|
{children}
|
|
31
|
-
</
|
|
41
|
+
</Element>
|
|
32
42
|
)
|
|
33
43
|
}
|
|
@@ -8,7 +8,7 @@ export type InputType = (typeof InputTypes)[number]
|
|
|
8
8
|
export type InputStatusType = (typeof InputStatus)[number]
|
|
9
9
|
|
|
10
10
|
export type InputProps = {
|
|
11
|
-
inputRef?: React.
|
|
11
|
+
inputRef?: React.Ref<HTMLInputElement>
|
|
12
12
|
status?: InputStatusType
|
|
13
13
|
startIconAdornment?: React.ReactNode
|
|
14
14
|
endIconAdornment?: React.ReactNode
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React, { useId, useRef, type HTMLAttributes } from 'react'
|
|
2
|
+
import { mergeRefs } from '@react-aria/utils'
|
|
2
3
|
import classnames from 'classnames'
|
|
3
4
|
import { type ReactFocusOnProps } from 'react-focus-on/dist/es5/types'
|
|
4
5
|
import { FieldMessage, type FieldMessageProps } from '~components/FieldMessage'
|
|
@@ -14,6 +15,7 @@ import { type MultiSelectOption, type ValidationMessage } from './types'
|
|
|
14
15
|
import styles from './MultiSelect.module.scss'
|
|
15
16
|
|
|
16
17
|
export type MultiSelectProps = {
|
|
18
|
+
inputRef?: React.Ref<HTMLButtonElement>
|
|
17
19
|
label: string
|
|
18
20
|
items: MultiSelectOptionsProps['options']
|
|
19
21
|
selectedValues: Set<MultiSelectOption['value']>
|
|
@@ -39,6 +41,7 @@ export const MultiSelect = ({
|
|
|
39
41
|
onOpenChange,
|
|
40
42
|
classNameOverride,
|
|
41
43
|
validationMessage,
|
|
44
|
+
inputRef,
|
|
42
45
|
...restProps
|
|
43
46
|
}: MultiSelectProps): JSX.Element => {
|
|
44
47
|
const fallbackId = useId()
|
|
@@ -88,7 +91,7 @@ export const MultiSelect = ({
|
|
|
88
91
|
|
|
89
92
|
<div ref={refs.setReference} className={styles.toggleContainer}>
|
|
90
93
|
<MultiSelectToggle
|
|
91
|
-
ref={toggleButtonRef}
|
|
94
|
+
ref={mergeRefs(toggleButtonRef, inputRef)}
|
|
92
95
|
id={`${id}--toggle`}
|
|
93
96
|
aria-labelledby={`${id}--label`}
|
|
94
97
|
aria-describedby={`${validationId} ${descriptionId}`}
|
|
@@ -22,6 +22,7 @@ import styles from './RichTextEditor.module.scss'
|
|
|
22
22
|
|
|
23
23
|
type BaseRichTextEditorProps = {
|
|
24
24
|
id?: string
|
|
25
|
+
inputRef?: React.Ref<HTMLElement>
|
|
25
26
|
onChange: (content: ProseMirrorState.EditorState) => void
|
|
26
27
|
defaultValue: EditorContentArray
|
|
27
28
|
controls?: ToolbarItems[]
|
|
@@ -60,6 +61,7 @@ export type RichTextEditorProps = BaseRichTextEditorProps & (WithLabelText | Wit
|
|
|
60
61
|
*/
|
|
61
62
|
export const RichTextEditor = ({
|
|
62
63
|
id,
|
|
64
|
+
inputRef,
|
|
63
65
|
onChange,
|
|
64
66
|
defaultValue,
|
|
65
67
|
labelText,
|
|
@@ -105,6 +107,7 @@ export const RichTextEditor = ({
|
|
|
105
107
|
'role': 'textbox',
|
|
106
108
|
'aria-describedby': ariaDescribedBy,
|
|
107
109
|
},
|
|
110
|
+
{ inputRef },
|
|
108
111
|
)
|
|
109
112
|
} catch {
|
|
110
113
|
return new Error('Bad data error')
|
|
@@ -32,7 +32,7 @@ describe('createRichTextEditor()', () => {
|
|
|
32
32
|
initialEditorState: testEditorState,
|
|
33
33
|
})
|
|
34
34
|
|
|
35
|
-
expect(Object.keys(returnValue)).toEqual(['destroy', 'dispatchTransaction'])
|
|
35
|
+
expect(Object.keys(returnValue)).toEqual(['destroy', 'dispatchTransaction', 'dom'])
|
|
36
36
|
expect(typeof returnValue.destroy).toEqual('function')
|
|
37
37
|
expect(typeof returnValue.dispatchTransaction).toEqual('function')
|
|
38
38
|
})
|
|
@@ -5,6 +5,7 @@ import { type CommandOrTransaction } from './types'
|
|
|
5
5
|
type EditorAPI = {
|
|
6
6
|
destroy: () => void
|
|
7
7
|
dispatchTransaction: (maybeCommand: CommandOrTransaction) => void
|
|
8
|
+
dom: HTMLElement
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
type EditorArgs = {
|
|
@@ -60,5 +61,6 @@ export const createRichTextEditor = ({
|
|
|
60
61
|
return {
|
|
61
62
|
destroy: () => editorView.destroy(),
|
|
62
63
|
dispatchTransaction: dispatchCommandOrTransaction,
|
|
64
|
+
dom: editorView.dom,
|
|
63
65
|
}
|
|
64
66
|
}
|
|
@@ -10,9 +10,11 @@ const user = userEvent.setup()
|
|
|
10
10
|
const Scenario = ({
|
|
11
11
|
onChange = () => undefined,
|
|
12
12
|
editable = true,
|
|
13
|
+
inputRef,
|
|
13
14
|
}: {
|
|
14
15
|
onChange?: (editorState: EditorState) => void
|
|
15
16
|
editable?: boolean
|
|
17
|
+
inputRef?: React.Ref<HTMLElement>
|
|
16
18
|
}): JSX.Element => {
|
|
17
19
|
const command: Command = (state, dispatch) => {
|
|
18
20
|
// Insert text at the current selection point, which is the start because
|
|
@@ -25,7 +27,7 @@ const Scenario = ({
|
|
|
25
27
|
const [ref, editorState, dispatchTransaction, setEditableStatus] = useRichTextEditor(
|
|
26
28
|
testEditorState,
|
|
27
29
|
{ 'aria-labelledby': 'label-ref-id', 'data-testid': '12345678' },
|
|
28
|
-
{ editable },
|
|
30
|
+
{ editable, inputRef },
|
|
29
31
|
)
|
|
30
32
|
|
|
31
33
|
useEffect(() => {
|
|
@@ -151,14 +153,17 @@ describe('useRichTextEditor()', () => {
|
|
|
151
153
|
})
|
|
152
154
|
})
|
|
153
155
|
|
|
154
|
-
it('
|
|
155
|
-
const
|
|
156
|
+
it('assigns ref on mount and cleans up DOM/ref on unmount', async () => {
|
|
157
|
+
const inputRef = React.createRef<HTMLElement>()
|
|
158
|
+
const { unmount } = render(<Scenario inputRef={inputRef} />)
|
|
156
159
|
|
|
157
160
|
const editor = screen.getByTestId('testid--editor')
|
|
158
161
|
expect(editor).toBeInTheDocument()
|
|
162
|
+
expect(inputRef.current).toHaveAttribute('contenteditable', 'true')
|
|
159
163
|
|
|
160
164
|
unmount()
|
|
161
165
|
|
|
162
166
|
expect(screen.queryByTestId('testid--editor')).not.toBeInTheDocument()
|
|
167
|
+
expect(inputRef.current).toBeNull()
|
|
163
168
|
})
|
|
164
169
|
})
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { useCallback, useRef, useState } from 'react'
|
|
2
2
|
import { type EditorState } from 'prosemirror-state'
|
|
3
|
+
import { isRefObject } from '~components/utils/isRefObject'
|
|
3
4
|
import { createRichTextEditor } from '../createRichTextEditor'
|
|
4
5
|
import { type CommandOrTransaction } from '../types'
|
|
5
6
|
|
|
6
7
|
type RTEOptions = {
|
|
7
|
-
editable
|
|
8
|
+
editable?: boolean
|
|
9
|
+
inputRef?: React.Ref<HTMLElement>
|
|
8
10
|
}
|
|
9
11
|
|
|
10
12
|
type SetEditableStatus = (status: boolean) => void
|
|
@@ -30,12 +32,8 @@ export const useRichTextEditor = (
|
|
|
30
32
|
* Pass in HTML attributes into the parent RTE node
|
|
31
33
|
*/
|
|
32
34
|
attributes?: Record<string, string>,
|
|
33
|
-
|
|
35
|
+
{ editable = true, inputRef }: RTEOptions = {},
|
|
34
36
|
): UseRichTextEditorReturnValue => {
|
|
35
|
-
options = {
|
|
36
|
-
editable: true,
|
|
37
|
-
...options,
|
|
38
|
-
}
|
|
39
37
|
const [editorState, setEditorState] = useState<EditorState>(initialEditorState)
|
|
40
38
|
// Refs to hold the methods returned from ProseMirror’s initialization
|
|
41
39
|
const destroyEditorRef = useRef<() => void>()
|
|
@@ -53,7 +51,11 @@ export const useRichTextEditor = (
|
|
|
53
51
|
)
|
|
54
52
|
|
|
55
53
|
// Hold editableStatus as a ref so we can toggle its status
|
|
56
|
-
const editableStatusRef = useRef<boolean>(
|
|
54
|
+
const editableStatusRef = useRef<boolean>(editable)
|
|
55
|
+
|
|
56
|
+
// Stable ref to avoid recreating editorRef when inputRef callback identity changes
|
|
57
|
+
const inputRefRef = useRef(inputRef)
|
|
58
|
+
inputRefRef.current = inputRef
|
|
57
59
|
const setEditableStatus = useCallback<SetEditableStatus>(
|
|
58
60
|
(status) => {
|
|
59
61
|
editableStatusRef.current = status
|
|
@@ -76,6 +78,11 @@ export const useRichTextEditor = (
|
|
|
76
78
|
destroyEditorRef.current()
|
|
77
79
|
destroyEditorRef.current = undefined
|
|
78
80
|
}
|
|
81
|
+
if (inputRefRef.current && isRefObject(inputRefRef.current)) {
|
|
82
|
+
;(inputRefRef.current as React.MutableRefObject<HTMLElement | null>).current = null
|
|
83
|
+
} else {
|
|
84
|
+
inputRefRef.current?.(null)
|
|
85
|
+
}
|
|
79
86
|
return
|
|
80
87
|
}
|
|
81
88
|
|
|
@@ -88,6 +95,12 @@ export const useRichTextEditor = (
|
|
|
88
95
|
})
|
|
89
96
|
destroyEditorRef.current = instance.destroy
|
|
90
97
|
dispatchTransactionRef.current = instance.dispatchTransaction
|
|
98
|
+
|
|
99
|
+
if (inputRefRef.current && isRefObject(inputRefRef.current)) {
|
|
100
|
+
;(inputRefRef.current as React.MutableRefObject<HTMLElement | null>).current = instance.dom
|
|
101
|
+
} else {
|
|
102
|
+
inputRefRef.current?.(instance.dom)
|
|
103
|
+
}
|
|
91
104
|
},
|
|
92
105
|
|
|
93
106
|
// Including editorState in the dependencies here will cause an endless
|