@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/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@linzjs/step-ag-grid",
3
3
  "repository": "github:linz/step-ag-grid.git",
4
4
  "license": "MIT",
5
- "version": "32.4.2",
5
+ "version": "32.5.0",
6
6
  "keywords": [
7
7
  "aggrid",
8
8
  "ag-grid",
@@ -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) => setValue(e.target.value)}
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) => setValue(e.target.value)}
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 }}
@@ -2,6 +2,7 @@
2
2
 
3
3
  .FormError {
4
4
  display: flex;
5
+ line-height: 24px;
5
6
 
6
7
  &-helpText {
7
8
  font-size: 0.75rem;
@@ -1,8 +1,4 @@
1
- import '../../../react-menu3/styles/index.scss';
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 '../../../react-menu3/styles/index.scss';
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
  };