@linzjs/step-ag-grid 32.4.2 → 32.5.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/src/components/gridForm/GridFormTextArea.d.ts +1 -0
- package/dist/src/components/gridForm/GridFormTextInput.d.ts +1 -0
- package/dist/step-ag-grid.cjs +15 -3
- package/dist/step-ag-grid.cjs.map +1 -1
- package/dist/step-ag-grid.esm.js +16 -4
- package/dist/step-ag-grid.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/components/gridForm/GridFormTextArea.tsx +10 -1
- package/src/components/gridForm/GridFormTextInput.tsx +10 -1
- package/src/lui/FormError.scss +1 -0
- package/src/stories/grid/gridFormInteraction/GridFormTextAreaInteraction.stories.tsx +67 -13
- package/src/stories/grid/gridFormInteraction/GridFormTextInputInteraction.stories.tsx +69 -15
package/package.json
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useFilterCharacters } from '@linzjs/lui';
|
|
1
2
|
import { ReactElement, useCallback, useMemo, useState } from 'react';
|
|
2
3
|
|
|
3
4
|
import { useGridPopoverContext } from '../../contexts/GridPopoverContext';
|
|
@@ -13,6 +14,7 @@ export interface GridFormTextAreaProps<TData extends GridBaseRow>
|
|
|
13
14
|
width?: string | number;
|
|
14
15
|
onSave?: (props: { selectedRows: TData[]; value: string }) => Promise<boolean>;
|
|
15
16
|
helpText?: string;
|
|
17
|
+
disableUnicodeCharacterFilter?: boolean;
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
export const GridFormTextArea = <TData extends GridBaseRow>(props: GridFormTextAreaProps<TData>): ReactElement => {
|
|
@@ -21,6 +23,8 @@ export const GridFormTextArea = <TData extends GridBaseRow>(props: GridFormTextA
|
|
|
21
23
|
const initValue = useMemo(() => (initialVale == null ? '' : `${initialVale}`), [initialVale]);
|
|
22
24
|
const [value, setValue] = useState(initValue);
|
|
23
25
|
|
|
26
|
+
const { filterInputEvent } = useFilterCharacters();
|
|
27
|
+
|
|
24
28
|
const helpText = props.helpText ?? 'Press tab to save';
|
|
25
29
|
|
|
26
30
|
const invalid = useCallback(() => TextInputValidator(props, value, data, {}), [props, value, data]);
|
|
@@ -53,7 +57,12 @@ export const GridFormTextArea = <TData extends GridBaseRow>(props: GridFormTextA
|
|
|
53
57
|
<div className={'subComponent'} style={{ display: 'flex', flexDirection: 'row', width: props.width ?? 240 }}>
|
|
54
58
|
<TextAreaInput
|
|
55
59
|
value={value}
|
|
56
|
-
onChange={(e) =>
|
|
60
|
+
onChange={(e) => {
|
|
61
|
+
if (!props.disableUnicodeCharacterFilter) {
|
|
62
|
+
filterInputEvent(e);
|
|
63
|
+
}
|
|
64
|
+
setValue(e.target.value);
|
|
65
|
+
}}
|
|
57
66
|
error={invalid()}
|
|
58
67
|
placeholder={props.placeholder ?? 'Type here'}
|
|
59
68
|
helpText={helpText}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useFilterCharacters } from '@linzjs/lui';
|
|
1
2
|
import { useCallback, useMemo, useState } from 'react';
|
|
2
3
|
|
|
3
4
|
import { useGridPopoverContext } from '../../contexts/GridPopoverContext';
|
|
@@ -18,6 +19,7 @@ export interface GridFormTextInputProps<TData extends GridBaseRow>
|
|
|
18
19
|
width?: string | number;
|
|
19
20
|
onSave?: (props: { selectedRows: TData[]; value: string }) => Promise<boolean>;
|
|
20
21
|
helpText?: string;
|
|
22
|
+
disableUnicodeCharacterFilter?: boolean;
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
export const GridFormTextInput = <TData extends GridBaseRow>(props: GridFormTextInputProps<TData>) => {
|
|
@@ -28,6 +30,8 @@ export const GridFormTextInput = <TData extends GridBaseRow>(props: GridFormText
|
|
|
28
30
|
const initValue = useMemo(() => (initialVale == null ? '' : `${initialVale}`), [initialVale]);
|
|
29
31
|
const [value, setValue] = useState(initValue);
|
|
30
32
|
|
|
33
|
+
const { filterInputEvent } = useFilterCharacters();
|
|
34
|
+
|
|
31
35
|
const invalid = useCallback(() => TextInputValidator<TData>(props, value, data, {}), [data, props, value]);
|
|
32
36
|
|
|
33
37
|
const save = useCallback(
|
|
@@ -61,7 +65,12 @@ export const GridFormTextInput = <TData extends GridBaseRow>(props: GridFormText
|
|
|
61
65
|
<div style={{ display: 'flex', flexDirection: 'row' }} className={'FormTest subComponent'}>
|
|
62
66
|
<TextInputFormatted
|
|
63
67
|
value={value}
|
|
64
|
-
onChange={(e) =>
|
|
68
|
+
onChange={(e) => {
|
|
69
|
+
if (!props.disableUnicodeCharacterFilter) {
|
|
70
|
+
filterInputEvent(e);
|
|
71
|
+
}
|
|
72
|
+
setValue(e.target.value);
|
|
73
|
+
}}
|
|
65
74
|
error={invalid()}
|
|
66
75
|
formatted={props.units}
|
|
67
76
|
style={{ width: props.width ?? 240 }}
|
package/src/lui/FormError.scss
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
import '
|
|
2
|
-
import '../../../styles/index.scss';
|
|
3
|
-
import '@linzjs/lui/dist/scss/base.scss';
|
|
4
|
-
import '@linzjs/lui/dist/fonts';
|
|
5
|
-
|
|
1
|
+
import { LuiFilterCharactersProvider } from '@linzjs/lui';
|
|
6
2
|
import { StoryFn } from '@storybook/react-vite';
|
|
7
3
|
import { GridPopoverContext } from '../../../contexts/GridPopoverContext';
|
|
8
4
|
import { useRef } from 'react';
|
|
@@ -51,35 +47,93 @@ export const GridFormTextAreaInteractions_: typeof Template = Template.bind({});
|
|
|
51
47
|
GridFormTextAreaInteractions_.play = async ({ canvasElement }) => {
|
|
52
48
|
const canvas = within(canvasElement);
|
|
53
49
|
|
|
54
|
-
expect(await canvas.findByText('Must not be empty')).toBeInTheDocument();
|
|
50
|
+
await expect(await canvas.findByText('Must not be empty')).toBeInTheDocument();
|
|
55
51
|
|
|
56
52
|
const inputField = canvas.getByPlaceholderText('Type here');
|
|
57
53
|
await userEvent.type(inputField, 'Hello');
|
|
58
54
|
|
|
59
|
-
expect(await canvas.findByText('Press tab to save')).toBeInTheDocument();
|
|
55
|
+
await expect(await canvas.findByText('Press tab to save')).toBeInTheDocument();
|
|
60
56
|
|
|
61
57
|
// Test tab to save
|
|
62
58
|
updateValue.mockClear();
|
|
63
59
|
await userEvent.tab();
|
|
64
|
-
expect(updateValue).toHaveBeenCalledWith(expect.anything(), 1); // 1 = Tab
|
|
60
|
+
await expect(updateValue).toHaveBeenCalledWith(expect.anything(), 1); // 1 = Tab
|
|
65
61
|
|
|
66
62
|
// Test shift+tab to save
|
|
67
63
|
updateValue.mockClear();
|
|
68
64
|
await userEvent.tab({ shift: true });
|
|
69
|
-
expect(updateValue).toHaveBeenCalledWith(expect.anything(), -1); // -1 = Shift + tab
|
|
65
|
+
await expect(updateValue).toHaveBeenCalledWith(expect.anything(), -1); // -1 = Shift + tab
|
|
70
66
|
|
|
71
67
|
// Test escape not to save
|
|
72
68
|
updateValue.mockClear();
|
|
73
69
|
await userEvent.type(inputField, '{Escape}');
|
|
74
|
-
expect(updateValue).toHaveBeenCalledWith(expect.anything(), 0);
|
|
70
|
+
await expect(updateValue).toHaveBeenCalledWith(expect.anything(), 0);
|
|
75
71
|
|
|
76
72
|
// Test invalid value doesn't save
|
|
77
73
|
updateValue.mockClear();
|
|
78
74
|
await userEvent.clear(inputField);
|
|
79
75
|
|
|
80
|
-
expect(canvas.getByText('Must not be empty')).toBeInTheDocument();
|
|
76
|
+
await expect(canvas.getByText('Must not be empty')).toBeInTheDocument();
|
|
81
77
|
await userEvent.tab();
|
|
82
|
-
expect(updateValue).not.toHaveBeenCalled();
|
|
78
|
+
await expect(updateValue).not.toHaveBeenCalled();
|
|
83
79
|
await userEvent.tab({ shift: true });
|
|
84
|
-
expect(updateValue).not.toHaveBeenCalled();
|
|
80
|
+
await expect(updateValue).not.toHaveBeenCalled();
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const FilterTemplate: StoryFn<typeof GridFormTextArea> = (props: GridFormTextAreaProps<any>) => {
|
|
84
|
+
const anchorRef = useRef<HTMLHeadingElement>(null);
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<div className={'react-menu-inline-test'}>
|
|
88
|
+
<LuiFilterCharactersProvider>
|
|
89
|
+
<GridContextProvider>
|
|
90
|
+
<h6 ref={anchorRef}>Interaction Test</h6>
|
|
91
|
+
<GridPopoverContext.Provider
|
|
92
|
+
value={{
|
|
93
|
+
anchorRef,
|
|
94
|
+
value: null,
|
|
95
|
+
updateValue,
|
|
96
|
+
data: { value: null },
|
|
97
|
+
colId: '',
|
|
98
|
+
field: 'value',
|
|
99
|
+
selectedRows: [],
|
|
100
|
+
saving: false,
|
|
101
|
+
setSaving: () => {},
|
|
102
|
+
formatValue: (value) => value,
|
|
103
|
+
stopEditing: () => {},
|
|
104
|
+
}}
|
|
105
|
+
>
|
|
106
|
+
<GridFormTextArea {...props} />
|
|
107
|
+
</GridPopoverContext.Provider>
|
|
108
|
+
</GridContextProvider>
|
|
109
|
+
</LuiFilterCharactersProvider>
|
|
110
|
+
</div>
|
|
111
|
+
);
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
// By default (disableUnicodeCharacterFilter unset) invalid unicode characters are silently
|
|
115
|
+
// converted or stripped as the user types, matching LuiTextAreaInput's behaviour.
|
|
116
|
+
export const GridFormTextAreaUnicodeFilter_ = FilterTemplate.bind({});
|
|
117
|
+
GridFormTextAreaUnicodeFilter_.play = async ({ canvasElement }) => {
|
|
118
|
+
const canvas = within(canvasElement);
|
|
119
|
+
|
|
120
|
+
// The popover mounts asynchronously (GridPopoverHook opens on a post-mount effect), so wait for it.
|
|
121
|
+
const inputField = await canvas.findByPlaceholderText('Type here');
|
|
122
|
+
// ’ (U+2019 right single quote) -> ' , — (U+2014 em dash) -> - , Ω (U+03A9, out of range) -> removed
|
|
123
|
+
await userEvent.type(inputField, 'It’s—cool Ω');
|
|
124
|
+
|
|
125
|
+
await expect(inputField).toHaveValue("It's-cool ");
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// disableUnicodeCharacterFilter=true opts out of filtering, leaving invalid characters intact.
|
|
129
|
+
export const GridFormTextAreaUnicodeFilterDisabled_ = FilterTemplate.bind({});
|
|
130
|
+
GridFormTextAreaUnicodeFilterDisabled_.args = { disableUnicodeCharacterFilter: true };
|
|
131
|
+
GridFormTextAreaUnicodeFilterDisabled_.play = async ({ canvasElement }) => {
|
|
132
|
+
const canvas = within(canvasElement);
|
|
133
|
+
|
|
134
|
+
const inputField = await canvas.findByPlaceholderText('Type here');
|
|
135
|
+
// Same chars as above, but left untouched since filtering is disabled
|
|
136
|
+
await userEvent.type(inputField, 'It’s—cool Ω');
|
|
137
|
+
|
|
138
|
+
await expect(inputField).toHaveValue('It’s—cool Ω');
|
|
85
139
|
};
|
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
import '
|
|
2
|
-
import '../../../styles/index.scss';
|
|
3
|
-
import '@linzjs/lui/dist/scss/base.scss';
|
|
4
|
-
import '@linzjs/lui/dist/fonts';
|
|
5
|
-
|
|
1
|
+
import { LuiFilterCharactersProvider } from '@linzjs/lui';
|
|
6
2
|
import { StoryFn } from '@storybook/react-vite';
|
|
7
3
|
import { GridPopoverContext } from '../../../contexts/GridPopoverContext';
|
|
8
4
|
import { useRef } from 'react';
|
|
@@ -51,42 +47,100 @@ export const GridFormTextInputInteractions_: typeof Template = Template.bind({})
|
|
|
51
47
|
GridFormTextInputInteractions_.play = async ({ canvasElement }) => {
|
|
52
48
|
const canvas = within(canvasElement);
|
|
53
49
|
|
|
54
|
-
expect(await canvas.findByText('Must not be empty')).toBeInTheDocument();
|
|
50
|
+
await expect(await canvas.findByText('Must not be empty')).toBeInTheDocument();
|
|
55
51
|
|
|
56
52
|
const inputField = canvas.getByPlaceholderText('Type here');
|
|
57
53
|
await userEvent.type(inputField, 'Hello');
|
|
58
54
|
|
|
59
|
-
expect(canvas.getByText('Press enter or tab to save')).toBeInTheDocument();
|
|
55
|
+
await expect(canvas.getByText('Press enter or tab to save')).toBeInTheDocument();
|
|
60
56
|
|
|
61
57
|
// Test enter to save
|
|
62
58
|
updateValue.mockClear();
|
|
63
59
|
await userEvent.type(inputField, '{Enter}');
|
|
64
|
-
expect(updateValue).toHaveBeenCalledWith(expect.anything(), 0); // 0 = Enter
|
|
60
|
+
await expect(updateValue).toHaveBeenCalledWith(expect.anything(), 0); // 0 = Enter
|
|
65
61
|
|
|
66
62
|
// Test tab to save
|
|
67
63
|
updateValue.mockClear();
|
|
68
64
|
await userEvent.tab();
|
|
69
|
-
expect(updateValue).toHaveBeenCalledWith(expect.anything(), 1); // 1 = Tab
|
|
65
|
+
await expect(updateValue).toHaveBeenCalledWith(expect.anything(), 1); // 1 = Tab
|
|
70
66
|
|
|
71
67
|
// Test shift+tab to save
|
|
72
68
|
updateValue.mockClear();
|
|
73
69
|
await userEvent.tab({ shift: true });
|
|
74
|
-
expect(updateValue).toHaveBeenCalledWith(expect.anything(), -1); // -1 = Shift + tab
|
|
70
|
+
await expect(updateValue).toHaveBeenCalledWith(expect.anything(), -1); // -1 = Shift + tab
|
|
75
71
|
|
|
76
72
|
// Test escape not to save
|
|
77
73
|
updateValue.mockClear();
|
|
78
74
|
await userEvent.type(inputField, '{Escape}');
|
|
79
|
-
expect(updateValue).toHaveBeenCalledWith(expect.anything(), 0);
|
|
75
|
+
await expect(updateValue).toHaveBeenCalledWith(expect.anything(), 0);
|
|
80
76
|
|
|
81
77
|
// Test invalid value doesn't save
|
|
82
78
|
updateValue.mockClear();
|
|
83
79
|
await userEvent.clear(inputField);
|
|
84
80
|
|
|
85
|
-
expect(canvas.getByText('Must not be empty')).toBeInTheDocument();
|
|
81
|
+
await expect(canvas.getByText('Must not be empty')).toBeInTheDocument();
|
|
86
82
|
await userEvent.type(inputField, '{Enter}');
|
|
87
|
-
expect(updateValue).not.toHaveBeenCalled();
|
|
83
|
+
await expect(updateValue).not.toHaveBeenCalled();
|
|
88
84
|
await userEvent.tab();
|
|
89
|
-
expect(updateValue).not.toHaveBeenCalled();
|
|
85
|
+
await expect(updateValue).not.toHaveBeenCalled();
|
|
90
86
|
await userEvent.tab({ shift: true });
|
|
91
|
-
expect(updateValue).not.toHaveBeenCalled();
|
|
87
|
+
await expect(updateValue).not.toHaveBeenCalled();
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const FilterTemplate: StoryFn<typeof GridFormTextInput> = (props: GridFormTextInputProps<any>) => {
|
|
91
|
+
const anchorRef = useRef<HTMLHeadingElement>(null);
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<div className={'react-menu-inline-test'}>
|
|
95
|
+
<LuiFilterCharactersProvider>
|
|
96
|
+
<GridContextProvider>
|
|
97
|
+
<h6 ref={anchorRef}>Interaction Test</h6>
|
|
98
|
+
<GridPopoverContext.Provider
|
|
99
|
+
value={{
|
|
100
|
+
anchorRef,
|
|
101
|
+
value: null,
|
|
102
|
+
updateValue,
|
|
103
|
+
data: { value: null },
|
|
104
|
+
colId: '',
|
|
105
|
+
field: 'value',
|
|
106
|
+
selectedRows: [],
|
|
107
|
+
saving: false,
|
|
108
|
+
setSaving: () => {},
|
|
109
|
+
formatValue: (value) => value,
|
|
110
|
+
stopEditing: () => {},
|
|
111
|
+
}}
|
|
112
|
+
>
|
|
113
|
+
<GridFormTextInput {...props} />
|
|
114
|
+
</GridPopoverContext.Provider>
|
|
115
|
+
</GridContextProvider>
|
|
116
|
+
</LuiFilterCharactersProvider>
|
|
117
|
+
</div>
|
|
118
|
+
);
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
// By default (disableUnicodeCharacterFilter unset) invalid unicode characters are silently
|
|
122
|
+
// converted or stripped as the user types, matching LuiTextInput's behaviour.
|
|
123
|
+
export const GridFormTextInputUnicodeFilter_ = FilterTemplate.bind({});
|
|
124
|
+
GridFormTextInputUnicodeFilter_.play = async ({ canvasElement }) => {
|
|
125
|
+
const canvas = within(canvasElement);
|
|
126
|
+
|
|
127
|
+
// The popover mounts asynchronously (GridPopoverHook opens on a post-mount effect), so wait for it.
|
|
128
|
+
const inputField = await canvas.findByPlaceholderText('Type here');
|
|
129
|
+
// ’ (U+2019 right single quote) -> ' , — (U+2014 em dash) -> - , Ω (U+03A9, out of range) -> removed
|
|
130
|
+
await userEvent.type(inputField, 'It’s—cool Ω');
|
|
131
|
+
|
|
132
|
+
await expect(inputField).toHaveValue("It's-cool ");
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// disableUnicodeCharacterFilter=true opts out of filtering, leaving invalid characters intact.
|
|
136
|
+
export const GridFormTextInputUnicodeFilterDisabled_ = FilterTemplate.bind({});
|
|
137
|
+
GridFormTextInputUnicodeFilterDisabled_.args = { disableUnicodeCharacterFilter: true };
|
|
138
|
+
GridFormTextInputUnicodeFilterDisabled_.play = async ({ canvasElement }) => {
|
|
139
|
+
const canvas = within(canvasElement);
|
|
140
|
+
|
|
141
|
+
const inputField = await canvas.findByPlaceholderText('Type here');
|
|
142
|
+
// Same chars as above, but left untouched since filtering is disabled
|
|
143
|
+
await userEvent.type(inputField, 'It’s—cool Ω');
|
|
144
|
+
|
|
145
|
+
await expect(inputField).toHaveValue('It’s—cool Ω');
|
|
92
146
|
};
|