@zeedhi/teknisa-components-common 1.74.1 → 1.76.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.
@@ -896,6 +896,7 @@ class GridBase {
896
896
  {
897
897
  name: `${this.grid.name}_gridSearch`,
898
898
  component: 'ZdSearch',
899
+ cssClass: 'zd-grid-search',
899
900
  isVisible: `{{GridController_${this.grid.componentId}.showSearchInput}}`,
900
901
  },
901
902
  {
@@ -973,6 +974,7 @@ class GridBase {
973
974
  fullscreen: mergeModalFilterProps.fullscreen,
974
975
  light: mergeModalFilterProps.light,
975
976
  dark: mergeModalFilterProps.dark,
977
+ escKeydownStop: false,
976
978
  children: [
977
979
  {
978
980
  name: `${this.grid.name}-filter-header-container`,
@@ -1049,6 +1051,13 @@ class GridBase {
1049
1051
  name: `${this.grid.name}-filter-cancelButton`,
1050
1052
  component: 'ZdButton',
1051
1053
  label: 'CANCEL',
1054
+ keyMap: {
1055
+ esc: {
1056
+ event: this.hideFilterModal.bind(this),
1057
+ focus: true,
1058
+ input: true,
1059
+ },
1060
+ },
1052
1061
  outline: true,
1053
1062
  events: {
1054
1063
  click: this.hideFilterModal.bind(this),
@@ -1061,6 +1070,14 @@ class GridBase {
1061
1070
  events: {
1062
1071
  click: this.applyFilter.bind(this),
1063
1072
  },
1073
+ keyMap: {
1074
+ 'mod+enter': {
1075
+ event: this.applyFilter.bind(this),
1076
+ focus: true,
1077
+ input: true,
1078
+ stop: true,
1079
+ },
1080
+ },
1064
1081
  },
1065
1082
  ],
1066
1083
  },
@@ -1075,8 +1092,17 @@ class GridBase {
1075
1092
  }
1076
1093
  this.filterModal.show();
1077
1094
  }
1078
- hideFilterModal() {
1079
- this.filterModal.hide();
1095
+ hideFilterModal(params) {
1096
+ var _a, _b;
1097
+ const event = params === null || params === void 0 ? void 0 : params.event;
1098
+ if (event && (((_a = document.activeElement) === null || _a === void 0 ? void 0 : _a.id) === `${this.grid.name}-filter-cancelButton`
1099
+ || ((_b = document.activeElement) === null || _b === void 0 ? void 0 : _b.id) === `${this.grid.name}-filter-okButton`)) {
1100
+ this.filterModal.hide();
1101
+ event.stopPropagation();
1102
+ }
1103
+ else {
1104
+ this.filterModal.hide();
1105
+ }
1080
1106
  }
1081
1107
  sortFilterIndex(col1, col2) {
1082
1108
  const index1 = col1.filterIndex !== undefined ? col1.filterIndex : Number.MAX_SAFE_INTEGER;
@@ -1226,7 +1252,7 @@ class GridBase {
1226
1252
  if (event.defaultPrevented)
1227
1253
  return;
1228
1254
  filterFn(filter);
1229
- this.filterModal.hide();
1255
+ this.hideFilterModal({ event });
1230
1256
  }
1231
1257
  clearFilter() {
1232
1258
  const filterForm = Metadata.getInstance(`${this.grid.name}-filter-form`);
@@ -2238,7 +2264,7 @@ class TekGrid extends GridEditable {
2238
2264
  /* Show Columns button */
2239
2265
  this.columnsButton = false;
2240
2266
  /* Defines if the grid is the unique grid on screen */
2241
- this.mainGrid = false;
2267
+ this.mainGrid = true;
2242
2268
  /* Columns to be ignored on columns button */
2243
2269
  this.columnsButtonIgnore = [];
2244
2270
  /**
@@ -3196,6 +3222,10 @@ class TekTreeGrid extends TreeGridEditable {
3196
3222
  * @public
3197
3223
  */
3198
3224
  this.dragColumns = true;
3225
+ /**
3226
+ * Defines if the grid is the unique grid on screen
3227
+ */
3228
+ this.mainGrid = true;
3199
3229
  /**
3200
3230
  * Enables column resizing
3201
3231
  * @public
@@ -3252,17 +3282,58 @@ class TekTreeGrid extends TreeGridEditable {
3252
3282
  if (this.deleteButton === 'selection') {
3253
3283
  this.selectable = true;
3254
3284
  }
3285
+ this.keyShortcutKeyMapping = {
3286
+ 'mod+f': {
3287
+ event: this.focusSearchInput.bind(this),
3288
+ stop: true,
3289
+ active: !this.mainGrid,
3290
+ input: true,
3291
+ },
3292
+ 'mod+l': {
3293
+ event: ({ event, element }) => {
3294
+ if (this.filterButton) {
3295
+ const instance = Metadata.getInstance(`${this.name}_filterButton`);
3296
+ instance.click(event, element);
3297
+ }
3298
+ },
3299
+ stop: true,
3300
+ active: !this.mainGrid,
3301
+ input: true,
3302
+ },
3303
+ 'mod+enter': {
3304
+ event: () => {
3305
+ const instance = Metadata.getInstance(`${this.name}_actions_dropdown`);
3306
+ instance.setFocus();
3307
+ instance.value = !instance.value;
3308
+ },
3309
+ stop: true,
3310
+ active: !this.mainGrid,
3311
+ input: true,
3312
+ },
3313
+ };
3255
3314
  this.gridBase = new GridBase(this);
3256
3315
  this.filterOperationsDatasource = this.gridBase.getFilterOperationsDatasource();
3257
3316
  this.filterRelationsDatasource = this.gridBase.getFilterRelationsDatasource();
3258
3317
  this.createAccessors();
3259
3318
  }
3319
+ onMounted(element) {
3320
+ super.onMounted(element);
3321
+ KeyMap.bind(this.keyShortcutKeyMapping, this, element);
3322
+ }
3323
+ onBeforeDestroy() {
3324
+ super.onBeforeDestroy();
3325
+ KeyMap.unbind(this.keyShortcutKeyMapping, this);
3326
+ }
3260
3327
  onCreated() {
3261
3328
  super.onCreated();
3262
3329
  Loader.addController(`GridController_${this.componentId}`, new GridController(this));
3263
3330
  if (!this.toolbarSlotProps)
3264
3331
  this.toolbarSlot = this.gridBase.createToolbarProps();
3265
3332
  }
3333
+ focusSearchInput() {
3334
+ const searchInput = Metadata.getInstance(`${this.name}_gridSearch`);
3335
+ searchInput.setFocus();
3336
+ }
3266
3337
  get layoutOptions() {
3267
3338
  if (!this.showLayoutOptions)
3268
3339
  return undefined;
@@ -901,6 +901,7 @@
901
901
  {
902
902
  name: `${this.grid.name}_gridSearch`,
903
903
  component: 'ZdSearch',
904
+ cssClass: 'zd-grid-search',
904
905
  isVisible: `{{GridController_${this.grid.componentId}.showSearchInput}}`,
905
906
  },
906
907
  {
@@ -978,6 +979,7 @@
978
979
  fullscreen: mergeModalFilterProps.fullscreen,
979
980
  light: mergeModalFilterProps.light,
980
981
  dark: mergeModalFilterProps.dark,
982
+ escKeydownStop: false,
981
983
  children: [
982
984
  {
983
985
  name: `${this.grid.name}-filter-header-container`,
@@ -1054,6 +1056,13 @@
1054
1056
  name: `${this.grid.name}-filter-cancelButton`,
1055
1057
  component: 'ZdButton',
1056
1058
  label: 'CANCEL',
1059
+ keyMap: {
1060
+ esc: {
1061
+ event: this.hideFilterModal.bind(this),
1062
+ focus: true,
1063
+ input: true,
1064
+ },
1065
+ },
1057
1066
  outline: true,
1058
1067
  events: {
1059
1068
  click: this.hideFilterModal.bind(this),
@@ -1066,6 +1075,14 @@
1066
1075
  events: {
1067
1076
  click: this.applyFilter.bind(this),
1068
1077
  },
1078
+ keyMap: {
1079
+ 'mod+enter': {
1080
+ event: this.applyFilter.bind(this),
1081
+ focus: true,
1082
+ input: true,
1083
+ stop: true,
1084
+ },
1085
+ },
1069
1086
  },
1070
1087
  ],
1071
1088
  },
@@ -1080,8 +1097,17 @@
1080
1097
  }
1081
1098
  this.filterModal.show();
1082
1099
  }
1083
- hideFilterModal() {
1084
- this.filterModal.hide();
1100
+ hideFilterModal(params) {
1101
+ var _a, _b;
1102
+ const event = params === null || params === void 0 ? void 0 : params.event;
1103
+ if (event && (((_a = document.activeElement) === null || _a === void 0 ? void 0 : _a.id) === `${this.grid.name}-filter-cancelButton`
1104
+ || ((_b = document.activeElement) === null || _b === void 0 ? void 0 : _b.id) === `${this.grid.name}-filter-okButton`)) {
1105
+ this.filterModal.hide();
1106
+ event.stopPropagation();
1107
+ }
1108
+ else {
1109
+ this.filterModal.hide();
1110
+ }
1085
1111
  }
1086
1112
  sortFilterIndex(col1, col2) {
1087
1113
  const index1 = col1.filterIndex !== undefined ? col1.filterIndex : Number.MAX_SAFE_INTEGER;
@@ -1231,7 +1257,7 @@
1231
1257
  if (event.defaultPrevented)
1232
1258
  return;
1233
1259
  filterFn(filter);
1234
- this.filterModal.hide();
1260
+ this.hideFilterModal({ event });
1235
1261
  }
1236
1262
  clearFilter() {
1237
1263
  const filterForm = core.Metadata.getInstance(`${this.grid.name}-filter-form`);
@@ -2243,7 +2269,7 @@
2243
2269
  /* Show Columns button */
2244
2270
  this.columnsButton = false;
2245
2271
  /* Defines if the grid is the unique grid on screen */
2246
- this.mainGrid = false;
2272
+ this.mainGrid = true;
2247
2273
  /* Columns to be ignored on columns button */
2248
2274
  this.columnsButtonIgnore = [];
2249
2275
  /**
@@ -3201,6 +3227,10 @@
3201
3227
  * @public
3202
3228
  */
3203
3229
  this.dragColumns = true;
3230
+ /**
3231
+ * Defines if the grid is the unique grid on screen
3232
+ */
3233
+ this.mainGrid = true;
3204
3234
  /**
3205
3235
  * Enables column resizing
3206
3236
  * @public
@@ -3257,17 +3287,58 @@
3257
3287
  if (this.deleteButton === 'selection') {
3258
3288
  this.selectable = true;
3259
3289
  }
3290
+ this.keyShortcutKeyMapping = {
3291
+ 'mod+f': {
3292
+ event: this.focusSearchInput.bind(this),
3293
+ stop: true,
3294
+ active: !this.mainGrid,
3295
+ input: true,
3296
+ },
3297
+ 'mod+l': {
3298
+ event: ({ event, element }) => {
3299
+ if (this.filterButton) {
3300
+ const instance = core.Metadata.getInstance(`${this.name}_filterButton`);
3301
+ instance.click(event, element);
3302
+ }
3303
+ },
3304
+ stop: true,
3305
+ active: !this.mainGrid,
3306
+ input: true,
3307
+ },
3308
+ 'mod+enter': {
3309
+ event: () => {
3310
+ const instance = core.Metadata.getInstance(`${this.name}_actions_dropdown`);
3311
+ instance.setFocus();
3312
+ instance.value = !instance.value;
3313
+ },
3314
+ stop: true,
3315
+ active: !this.mainGrid,
3316
+ input: true,
3317
+ },
3318
+ };
3260
3319
  this.gridBase = new GridBase(this);
3261
3320
  this.filterOperationsDatasource = this.gridBase.getFilterOperationsDatasource();
3262
3321
  this.filterRelationsDatasource = this.gridBase.getFilterRelationsDatasource();
3263
3322
  this.createAccessors();
3264
3323
  }
3324
+ onMounted(element) {
3325
+ super.onMounted(element);
3326
+ core.KeyMap.bind(this.keyShortcutKeyMapping, this, element);
3327
+ }
3328
+ onBeforeDestroy() {
3329
+ super.onBeforeDestroy();
3330
+ core.KeyMap.unbind(this.keyShortcutKeyMapping, this);
3331
+ }
3265
3332
  onCreated() {
3266
3333
  super.onCreated();
3267
3334
  core.Loader.addController(`GridController_${this.componentId}`, new GridController(this));
3268
3335
  if (!this.toolbarSlotProps)
3269
3336
  this.toolbarSlot = this.gridBase.createToolbarProps();
3270
3337
  }
3338
+ focusSearchInput() {
3339
+ const searchInput = core.Metadata.getInstance(`${this.name}_gridSearch`);
3340
+ searchInput.setFocus();
3341
+ }
3271
3342
  get layoutOptions() {
3272
3343
  if (!this.showLayoutOptions)
3273
3344
  return undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeedhi/teknisa-components-common",
3
- "version": "1.74.1",
3
+ "version": "1.76.0",
4
4
  "description": "Teknisa Components Common",
5
5
  "author": "Zeedhi <zeedhi@teknisa.com>",
6
6
  "license": "ISC",
@@ -18,18 +18,18 @@
18
18
  "watch": "npm run build -- -w"
19
19
  },
20
20
  "dependencies": {
21
- "@zeedhi/zd-user-info-common": "^1.0.2",
22
- "lodash.debounce": "^4.0.8",
23
- "lodash.get": "^4.4.2",
24
- "lodash.merge": "^4.6.2"
21
+ "@zeedhi/zd-user-info-common": "*",
22
+ "lodash.debounce": "4.0.*",
23
+ "lodash.get": "4.4.*",
24
+ "lodash.merge": "4.6.*"
25
25
  },
26
26
  "devDependencies": {
27
- "@types/lodash.debounce": "^4.0.6",
28
- "@types/lodash.get": "^4.4.6",
29
- "@types/lodash.merge": "^4.6.6"
27
+ "@types/lodash.debounce": "4.0.*",
28
+ "@types/lodash.get": "4.4.*",
29
+ "@types/lodash.merge": "4.6.*"
30
30
  },
31
31
  "peerDependencies": {
32
32
  "@zeedhi/core": "*"
33
33
  },
34
- "gitHead": "371e90750bf8c1703398f18838062e67a2993a8d"
34
+ "gitHead": "16a404b1342b88f2f01d2faf1c40216c3e1d6144"
35
35
  }
@@ -1684,6 +1684,17 @@ describe('TekGrid', () => {
1684
1684
  expect(button.isVisible).toBeFalsy();
1685
1685
  (instance as any).gridBase.hideFilterModal();
1686
1686
 
1687
+ const element = document.createElement('div');
1688
+ element.id = `${instance.name}-filter-cancelButton`;
1689
+
1690
+ Object.defineProperty(document, 'activeElement', { value: element, writable: true });
1691
+ let event = new Event('keydown');
1692
+ (instance as any).gridBase.hideFilterModal({ event });
1693
+
1694
+ (document as any).activeElement = undefined;
1695
+ event = new Event('keydown');
1696
+ (instance as any).gridBase.hideFilterModal({ event });
1697
+
1687
1698
  spy.mockReset();
1688
1699
  spyMetadata.mockReset();
1689
1700
  });
@@ -1,7 +1,9 @@
1
1
  import {
2
- Button, Form, IButton, IForm, IModal, Modal, ModalService, Report, Text, TextInput,
2
+ Button, Dropdown, Form, IButton, IForm, IModal, Modal, ModalService, Report, Search, Text, TextInput,
3
3
  } from '@zeedhi/common';
4
- import { Http, IDictionary, Metadata } from '@zeedhi/core';
4
+ import {
5
+ Http, IDictionary, KeyMap, Metadata,
6
+ } from '@zeedhi/core';
5
7
  import {
6
8
  ITekTreeGrid, TekGridColumn, TekRestDatasource, TekTreeGrid,
7
9
  } from '../../../../src';
@@ -111,6 +113,121 @@ describe('TekTreeGrid', () => {
111
113
  });
112
114
  });
113
115
 
116
+ describe('navigation', () => {
117
+ const dispatchEvent = (key: string, modifiers: any) => {
118
+ const event = new KeyboardEvent('keydown', modifiers);
119
+ delete (event as any).key;
120
+ Object.defineProperty(event, 'key', { value: key });
121
+ delete (event as any).code;
122
+ Object.defineProperty(event, 'code', { value: key });
123
+ document.dispatchEvent(event);
124
+ };
125
+
126
+ it('should open filter modal input with keymap', async () => {
127
+ const instance = new TekTreeGrid({
128
+ name: 'grid_navigation_2',
129
+ component: 'TekGrid',
130
+ filterButton: true,
131
+ });
132
+ const elem = document.createElement('DIV');
133
+
134
+ (KeyMap as any).isElementActive = () => true;
135
+
136
+ instance.onMounted(elem);
137
+ await flushPromises();
138
+
139
+ const method = jest.fn();
140
+
141
+ const spyMetadata = jest.spyOn(Metadata, 'getInstance').mockImplementation(() => new Button({
142
+ name: 'grid_navigation_2_filterButton',
143
+ component: 'ZdButton',
144
+ events: {
145
+ click: method,
146
+ },
147
+ }));
148
+
149
+ dispatchEvent('l', { ctrlKey: true }); // ctrl+l keydown
150
+ await flushPromises();
151
+
152
+ instance.onBeforeDestroy();
153
+ await flushPromises();
154
+
155
+ expect(method).toHaveBeenCalled();
156
+ (KeyMap as any).isElementActive = () => false;
157
+ spyMetadata.mockReset();
158
+ });
159
+
160
+ it('should open action dropdown with keymap', async () => {
161
+ const instance = new TekTreeGrid({
162
+ name: 'grid_navigation_2',
163
+ component: 'TekTreeGrid',
164
+ });
165
+ const elem = document.createElement('DIV');
166
+
167
+ (KeyMap as any).isElementActive = () => true;
168
+
169
+ instance.onMounted(elem);
170
+ await flushPromises();
171
+
172
+ const instanceDropDown = new Dropdown({
173
+ name: 'grid_navigation_2_actions_dropdown',
174
+ component: 'ZdDropdown',
175
+ activator: {
176
+ name: 'grid_navigation_2_actionsButton',
177
+ component: 'ZdButton',
178
+ },
179
+ children: [],
180
+ });
181
+ const instanceDropDownSpy = jest.spyOn(instanceDropDown, 'setFocus').mockImplementation(() => true);
182
+ const spyMetadata = jest.spyOn(Metadata, 'getInstance').mockImplementation(() => instanceDropDown);
183
+
184
+ dispatchEvent('enter', { ctrlKey: true, enterKey: true }); // ctrl + enter keydown
185
+ await flushPromises();
186
+
187
+ expect(spyMetadata).toHaveBeenCalled();
188
+ expect(instanceDropDownSpy).toHaveBeenCalled();
189
+
190
+ instance.onBeforeDestroy();
191
+ await flushPromises();
192
+
193
+ (KeyMap as any).isElementActive = () => false;
194
+ spyMetadata.mockReset();
195
+ instanceDropDownSpy.mockReset();
196
+ });
197
+
198
+ it('should focus in searchInput with keymap', async () => {
199
+ const instance = new TekTreeGrid({
200
+ name: 'grid_navigation_3',
201
+ component: 'TekTreeGrid',
202
+ });
203
+ const elem = document.createElement('DIV');
204
+
205
+ (KeyMap as any).isElementActive = () => true;
206
+ instance.onMounted(elem);
207
+ await flushPromises();
208
+
209
+ const searchInput = new Search({
210
+ name: 'grid_navigation_3_gridSearch',
211
+ component: 'ZdSearch',
212
+ parent: instance,
213
+ });
214
+
215
+ const spySetFocus = jest.spyOn(searchInput, 'setFocus').mockImplementation(() => true);
216
+ const spyMetadata = jest.spyOn(Metadata, 'getInstance').mockImplementation(() => searchInput);
217
+
218
+ dispatchEvent('f', { ctrlKey: true }); // ctrl+f keydown
219
+ await flushPromises();
220
+
221
+ expect(spySetFocus).toHaveBeenCalled();
222
+
223
+ instance.onBeforeDestroy();
224
+ await flushPromises();
225
+
226
+ (KeyMap as any).isElementActive = () => false;
227
+ spySetFocus.mockReset();
228
+ spyMetadata.mockReset();
229
+ });
230
+ });
114
231
  describe('filterClick()', () => {
115
232
  it('should apply filter from form to tekdatasource', () => {
116
233
  const httpSpy = jest.spyOn(Http, 'get').mockImplementation(() => Promise.resolve({
@@ -10,6 +10,7 @@ export interface ITekTreeGrid extends ITreeGridEditable {
10
10
  deleteButton?: 'none' | 'currentRow' | 'selection';
11
11
  events?: ITekGridEvents;
12
12
  filterButton?: boolean;
13
+ mainGrid?: boolean;
13
14
  modalFilterProps?: IModalFilterProps;
14
15
  showSearch?: boolean;
15
16
  showLayoutOptions?: boolean;
@@ -22,6 +22,10 @@ export declare class TekTreeGrid extends TreeGridEditable implements ITekTreeGri
22
22
  * @public
23
23
  */
24
24
  dragColumns: boolean;
25
+ /**
26
+ * Defines if the grid is the unique grid on screen
27
+ */
28
+ mainGrid: boolean;
25
29
  /**
26
30
  * Enables column resizing
27
31
  * @public
@@ -44,6 +48,7 @@ export declare class TekTreeGrid extends TreeGridEditable implements ITekTreeGri
44
48
  * Show refresh button
45
49
  */
46
50
  showReload: boolean;
51
+ protected keyShortcutKeyMapping: any;
47
52
  /**
48
53
  * Export config
49
54
  */
@@ -61,7 +66,10 @@ export declare class TekTreeGrid extends TreeGridEditable implements ITekTreeGri
61
66
  * @param props TekTreeGrid properties
62
67
  */
63
68
  constructor(props: ITekTreeGrid);
69
+ onMounted(element: HTMLElement): void;
70
+ onBeforeDestroy(): void;
64
71
  onCreated(): void;
72
+ protected focusSearchInput(): void;
65
73
  get layoutOptions(): TekGridLayoutOptions | undefined;
66
74
  /**
67
75
  * Get Grid columns objects
@@ -204,8 +204,8 @@ export declare class GridBase {
204
204
  } | {
205
205
  name: string;
206
206
  component: string;
207
+ cssClass: string;
207
208
  isVisible: string;
208
- cssClass?: undefined;
209
209
  tag?: undefined;
210
210
  text?: undefined;
211
211
  title?: undefined;
@@ -249,7 +249,9 @@ export declare class GridBase {
249
249
  filterClick({ event }: any): void;
250
250
  private filterModal?;
251
251
  private createFilterFromColumns;
252
- hideFilterModal(): void;
252
+ hideFilterModal(params?: {
253
+ event?: Event;
254
+ }): void;
253
255
  private sortFilterIndex;
254
256
  private getFilterModalComponents;
255
257
  private changeHelperEvent;