@oneuptime/common 12.0.7 → 12.0.8
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/Models/DatabaseModels/TelemetryEntity.ts +45 -0
- package/Models/DatabaseModels/TelemetryEntityRelationship.ts +26 -0
- package/Models/DatabaseModels/WorkflowLog.ts +38 -0
- package/Server/API/UserAPI.ts +16 -1
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786551733814-MigrationName.ts +41 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786559879134-AddWorkflowLogStepTrace.ts +17 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
- package/Server/Middleware/MasterAdminAuthorization.ts +11 -6
- package/Server/Services/TelemetryEntityRelationshipService.ts +3 -0
- package/Server/Services/TelemetryEntityService.ts +114 -1
- package/Server/Types/Workflow/Components/API/Utils.ts +44 -1
- package/Server/Types/Workflow/TriggerCode.ts +12 -0
- package/Server/Types/Workflow/Workflow.ts +5 -0
- package/Server/Utils/Telemetry/InventoryEntityRegistry.ts +689 -0
- package/Server/Utils/VM/VMAPI.ts +56 -8
- package/Tests/App/Dashboard/MonitorCriteriaAttributeFilter.test.tsx +574 -0
- package/Tests/Server/API/UserProjectsAPI.test.ts +478 -4
- package/Tests/Server/Services/TelemetryEntityManualCreate.test.ts +248 -0
- package/Tests/Server/Types/Workflow/Components/ApiComponentHeaders.test.ts +192 -0
- package/Tests/Server/Utils/AnalyticsDatabase/QuerySettingsHelper.test.ts +152 -0
- package/Tests/Server/Utils/Telemetry/InventoryEntityRegistry.test.ts +322 -0
- package/Tests/Server/Utils/VM/VMAPISubstitution.test.ts +243 -0
- package/Tests/Types/SerializableObjectDictionaryImportCycle.test.ts +197 -0
- package/Tests/Types/Telemetry/EntityTypeGroups.test.ts +140 -0
- package/Tests/Types/Workflow/StepTrace.test.ts +235 -0
- package/Tests/Types/Workflow/TemplateSyntax.test.ts +379 -0
- package/Tests/Types/Workflow/Templates.test.ts +403 -0
- package/Tests/UI/Components/CustomTimeRangeModal.test.tsx +459 -0
- package/Tests/UI/Components/DictionaryAttributeFilterRow.test.tsx +477 -0
- package/Tests/UI/Components/Forms/AnchoredFieldPopupKeyboard.test.tsx +382 -0
- package/Tests/UI/Components/Forms/ColorPickerPicking.test.tsx +569 -0
- package/Tests/UI/Components/Forms/ValidationJSON.test.ts +241 -0
- package/Tests/UI/Components/LogTimeRangePicker.test.tsx +42 -0
- package/Tests/UI/Components/ModalPortalDismissal.test.tsx +90 -0
- package/Tests/UI/Components/TelemetryTimeRangePicker.test.tsx +49 -7
- package/Tests/UI/Components/TimeRangePickerDropdown.test.tsx +325 -0
- package/Tests/UI/Components/Workflow/GraphLint.test.ts +821 -0
- package/Tests/UI/Components/Workflow/ModelColumnEditor.test.ts +287 -0
- package/Tests/UI/Components/Workflow/ModelColumnEditorServerContract.test.ts +185 -0
- package/Tests/UI/Components/Workflow/ModelSchema.test.ts +174 -0
- package/Tests/UI/Components/Workflow/RunStatusWatcher.test.ts +152 -0
- package/Tests/UI/Components/Workflow/Utils.test.ts +192 -0
- package/Tests/UI/Monitor/MonitorStepCriteriaView.test.tsx +432 -0
- package/Tests/Utils/Monitor/NetworkDeviceRoleUtil.test.ts +514 -0
- package/Tests/Utils/Monitor/NetworkTopologyUtil.test.ts +240 -0
- package/Tests/Utils/Schema/AnalyticsModelSchema.test.ts +469 -0
- package/Tests/Utils/Schema/ModelSchema.test.ts +268 -0
- package/Tests/Utils/Telemetry/EntityKeyNonTelemetry.test.ts +193 -0
- package/Types/Monitor/SnmpMonitor/NetworkTopology.ts +31 -0
- package/Types/SerializableObjectDictionary.ts +133 -39
- package/Types/Telemetry/EntitySource.ts +40 -0
- package/Types/Telemetry/EntityType.ts +27 -0
- package/Types/Telemetry/EntityTypeGroups.ts +65 -0
- package/Types/Workflow/Component.ts +29 -0
- package/Types/Workflow/Components/API.ts +35 -0
- package/Types/Workflow/Components/BaseModel.ts +7 -3
- package/Types/Workflow/StepTrace.ts +181 -0
- package/Types/Workflow/TemplateSyntax.ts +499 -0
- package/Types/Workflow/Templates.ts +316 -0
- package/UI/Components/Date/CustomTimeRangeModal.tsx +278 -0
- package/UI/Components/Date/TimeRangePickerDropdown.tsx +250 -0
- package/UI/Components/Dictionary/Dictionary.tsx +23 -6
- package/UI/Components/Forms/Fields/ColorPicker.tsx +66 -10
- package/UI/Components/Forms/Fields/IconPicker.tsx +48 -10
- package/UI/Components/Forms/Types/Field.ts +7 -0
- package/UI/Components/Forms/Validation.ts +63 -1
- package/UI/Components/Input/Input.tsx +31 -3
- package/UI/Components/LogsViewer/components/LogTimeRangePicker.tsx +11 -216
- package/UI/Components/Modal/Modal.tsx +37 -4
- package/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.tsx +11 -210
- package/UI/Components/Workflow/ArgumentsForm.tsx +194 -7
- package/UI/Components/Workflow/Component.tsx +28 -26
- package/UI/Components/Workflow/ComponentSettingsModal.tsx +69 -8
- package/UI/Components/Workflow/GraphLint.ts +591 -0
- package/UI/Components/Workflow/ModelColumnEditor.tsx +396 -0
- package/UI/Components/Workflow/ModelSchema.ts +196 -0
- package/UI/Components/Workflow/RunForm.tsx +41 -7
- package/UI/Components/Workflow/RunStatusWatcher.ts +122 -0
- package/UI/Components/Workflow/StepTraceViewer.tsx +186 -0
- package/UI/Components/Workflow/Utils.ts +93 -1
- package/UI/Components/Workflow/Workflow.tsx +77 -4
- package/UI/Types/LayeredDismissal.ts +31 -0
- package/UI/Types/UseAnchoredFieldPopup.ts +99 -0
- package/Utils/Monitor/NetworkDeviceRoleUtil.ts +533 -0
- package/Utils/Monitor/NetworkTopologyUtil.ts +45 -7
- package/Utils/Telemetry/EntityKey.ts +66 -0
- package/build/dist/Models/DatabaseModels/TelemetryEntity.js +47 -0
- package/build/dist/Models/DatabaseModels/TelemetryEntity.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TelemetryEntityRelationship.js +27 -0
- package/build/dist/Models/DatabaseModels/TelemetryEntityRelationship.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkflowLog.js +39 -0
- package/build/dist/Models/DatabaseModels/WorkflowLog.js.map +1 -1
- package/build/dist/Server/API/UserAPI.js +16 -1
- package/build/dist/Server/API/UserAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786551733814-MigrationName.js +20 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786551733814-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786559879134-AddWorkflowLogStepTrace.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786559879134-AddWorkflowLogStepTrace.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/MasterAdminAuthorization.js +11 -6
- package/build/dist/Server/Middleware/MasterAdminAuthorization.js.map +1 -1
- package/build/dist/Server/Services/TelemetryEntityRelationshipService.js +3 -0
- package/build/dist/Server/Services/TelemetryEntityRelationshipService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryEntityService.js +91 -1
- package/build/dist/Server/Services/TelemetryEntityService.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Utils.js +28 -0
- package/build/dist/Server/Types/Workflow/Components/API/Utils.js.map +1 -1
- package/build/dist/Server/Types/Workflow/TriggerCode.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js +507 -0
- package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js.map +1 -0
- package/build/dist/Server/Utils/VM/VMAPI.js +51 -4
- package/build/dist/Server/Utils/VM/VMAPI.js.map +1 -1
- package/build/dist/Types/SerializableObjectDictionary.js +133 -39
- package/build/dist/Types/SerializableObjectDictionary.js.map +1 -1
- package/build/dist/Types/Telemetry/EntitySource.js +39 -0
- package/build/dist/Types/Telemetry/EntitySource.js.map +1 -0
- package/build/dist/Types/Telemetry/EntityType.js +27 -0
- package/build/dist/Types/Telemetry/EntityType.js.map +1 -1
- package/build/dist/Types/Telemetry/EntityTypeGroups.js +57 -0
- package/build/dist/Types/Telemetry/EntityTypeGroups.js.map +1 -0
- package/build/dist/Types/Workflow/Component.js +21 -0
- package/build/dist/Types/Workflow/Component.js.map +1 -1
- package/build/dist/Types/Workflow/Components/API.js +35 -0
- package/build/dist/Types/Workflow/Components/API.js.map +1 -1
- package/build/dist/Types/Workflow/Components/BaseModel.js +7 -3
- package/build/dist/Types/Workflow/Components/BaseModel.js.map +1 -1
- package/build/dist/Types/Workflow/StepTrace.js +104 -0
- package/build/dist/Types/Workflow/StepTrace.js.map +1 -0
- package/build/dist/Types/Workflow/TemplateSyntax.js +326 -0
- package/build/dist/Types/Workflow/TemplateSyntax.js.map +1 -0
- package/build/dist/Types/Workflow/Templates.js +218 -0
- package/build/dist/Types/Workflow/Templates.js.map +1 -0
- package/build/dist/UI/Components/Date/CustomTimeRangeModal.js +126 -0
- package/build/dist/UI/Components/Date/CustomTimeRangeModal.js.map +1 -0
- package/build/dist/UI/Components/Date/TimeRangePickerDropdown.js +123 -0
- package/build/dist/UI/Components/Date/TimeRangePickerDropdown.js.map +1 -0
- package/build/dist/UI/Components/Dictionary/Dictionary.js +22 -6
- package/build/dist/UI/Components/Dictionary/Dictionary.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/ColorPicker.js +52 -13
- package/build/dist/UI/Components/Forms/Fields/ColorPicker.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/IconPicker.js +28 -12
- package/build/dist/UI/Components/Forms/Fields/IconPicker.js.map +1 -1
- package/build/dist/UI/Components/Forms/Validation.js +44 -0
- package/build/dist/UI/Components/Forms/Validation.js.map +1 -1
- package/build/dist/UI/Components/Input/Input.js +12 -4
- package/build/dist/UI/Components/Input/Input.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js +5 -108
- package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js.map +1 -1
- package/build/dist/UI/Components/Modal/Modal.js +31 -5
- package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js +5 -105
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ArgumentsForm.js +140 -11
- package/build/dist/UI/Components/Workflow/ArgumentsForm.js.map +1 -1
- package/build/dist/UI/Components/Workflow/Component.js +20 -19
- package/build/dist/UI/Components/Workflow/Component.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ComponentSettingsModal.js +31 -3
- package/build/dist/UI/Components/Workflow/ComponentSettingsModal.js.map +1 -1
- package/build/dist/UI/Components/Workflow/GraphLint.js +396 -0
- package/build/dist/UI/Components/Workflow/GraphLint.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ModelColumnEditor.js +225 -0
- package/build/dist/UI/Components/Workflow/ModelColumnEditor.js.map +1 -0
- package/build/dist/UI/Components/Workflow/ModelSchema.js +118 -0
- package/build/dist/UI/Components/Workflow/ModelSchema.js.map +1 -0
- package/build/dist/UI/Components/Workflow/RunForm.js +22 -6
- package/build/dist/UI/Components/Workflow/RunForm.js.map +1 -1
- package/build/dist/UI/Components/Workflow/RunStatusWatcher.js +76 -0
- package/build/dist/UI/Components/Workflow/RunStatusWatcher.js.map +1 -0
- package/build/dist/UI/Components/Workflow/StepTraceViewer.js +76 -0
- package/build/dist/UI/Components/Workflow/StepTraceViewer.js.map +1 -0
- package/build/dist/UI/Components/Workflow/Utils.js +73 -1
- package/build/dist/UI/Components/Workflow/Utils.js.map +1 -1
- package/build/dist/UI/Components/Workflow/Workflow.js +51 -6
- package/build/dist/UI/Components/Workflow/Workflow.js.map +1 -1
- package/build/dist/UI/Types/LayeredDismissal.js +21 -0
- package/build/dist/UI/Types/LayeredDismissal.js.map +1 -0
- package/build/dist/UI/Types/UseAnchoredFieldPopup.js +74 -1
- package/build/dist/UI/Types/UseAnchoredFieldPopup.js.map +1 -1
- package/build/dist/Utils/Monitor/NetworkDeviceRoleUtil.js +386 -0
- package/build/dist/Utils/Monitor/NetworkDeviceRoleUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +35 -6
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
- package/build/dist/Utils/Telemetry/EntityKey.js +52 -0
- package/build/dist/Utils/Telemetry/EntityKey.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
import BasicForm from "../../../../UI/Components/Forms/BasicForm";
|
|
2
|
+
import ColorPicker from "../../../../UI/Components/Forms/Fields/ColorPicker";
|
|
3
|
+
import IconPicker from "../../../../UI/Components/Forms/Fields/IconPicker";
|
|
4
|
+
import Fields from "../../../../UI/Components/Forms/Types/Fields";
|
|
5
|
+
import FormFieldSchemaType from "../../../../UI/Components/Forms/Types/FormFieldSchemaType";
|
|
6
|
+
import FormValues from "../../../../UI/Components/Forms/Types/FormValues";
|
|
7
|
+
import Modal from "../../../../UI/Components/Modal/Modal";
|
|
8
|
+
import Color from "../../../../Types/Color";
|
|
9
|
+
import IconProp from "../../../../Types/Icon/IconProp";
|
|
10
|
+
import getJestMockFunction, { MockFunction } from "../../../../Tests/MockType";
|
|
11
|
+
import "@testing-library/jest-dom";
|
|
12
|
+
import {
|
|
13
|
+
cleanup,
|
|
14
|
+
fireEvent,
|
|
15
|
+
render,
|
|
16
|
+
screen,
|
|
17
|
+
waitFor,
|
|
18
|
+
within,
|
|
19
|
+
} from "@testing-library/react";
|
|
20
|
+
import React from "react";
|
|
21
|
+
import { afterEach, describe, expect, test } from "@jest/globals";
|
|
22
|
+
|
|
23
|
+
/*
|
|
24
|
+
* The colour and icon fields are triggers dressed as text boxes: a readOnly
|
|
25
|
+
* input whose only job is to open a portalled popup, which it used to do on
|
|
26
|
+
* click and on nothing else. Every key was dead. That left a keyboard user with
|
|
27
|
+
* no way in at all — and on the Create Label form (issue #3143) the colour is
|
|
28
|
+
* required, so no way in meant no way to submit the form either.
|
|
29
|
+
*
|
|
30
|
+
* Both fields drive UseAnchoredFieldPopup, so the behaviour is stated once per
|
|
31
|
+
* field here and the shared parts are pinned in both.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
const OPENING_KEYS: Array<string> = ["Enter", " ", "ArrowDown", "ArrowUp"];
|
|
35
|
+
|
|
36
|
+
type RenderColorPickerFunction = (
|
|
37
|
+
props?: Partial<{ readOnly: boolean; disabled: boolean }>,
|
|
38
|
+
) => MockFunction;
|
|
39
|
+
|
|
40
|
+
const renderColorPicker: RenderColorPickerFunction = (
|
|
41
|
+
props: Partial<{ readOnly: boolean; disabled: boolean }> = {},
|
|
42
|
+
): MockFunction => {
|
|
43
|
+
const onChange: MockFunction = getJestMockFunction();
|
|
44
|
+
|
|
45
|
+
render(
|
|
46
|
+
<ColorPicker
|
|
47
|
+
dataTestId="color-value"
|
|
48
|
+
placeholder="Please select color for this label."
|
|
49
|
+
onChange={onChange}
|
|
50
|
+
readOnly={props.readOnly}
|
|
51
|
+
disabled={props.disabled}
|
|
52
|
+
tabIndex={0}
|
|
53
|
+
/>,
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
return onChange;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
type GetFieldFunction = (testId: string) => HTMLInputElement;
|
|
60
|
+
|
|
61
|
+
const getField: GetFieldFunction = (testId: string): HTMLInputElement => {
|
|
62
|
+
return screen.getByTestId(testId) as HTMLInputElement;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
describe("Opening an anchored field popup from the keyboard", () => {
|
|
66
|
+
afterEach(() => {
|
|
67
|
+
cleanup();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe("ColorPicker", () => {
|
|
71
|
+
test.each(OPENING_KEYS)(
|
|
72
|
+
"%p opens the popup and moves focus into it",
|
|
73
|
+
(key: string) => {
|
|
74
|
+
renderColorPicker();
|
|
75
|
+
|
|
76
|
+
const field: HTMLInputElement = getField("color-value");
|
|
77
|
+
|
|
78
|
+
field.focus();
|
|
79
|
+
fireEvent.keyDown(field, { key });
|
|
80
|
+
|
|
81
|
+
const popup: HTMLElement = screen.getByTestId("color-picker-popup");
|
|
82
|
+
|
|
83
|
+
expect(popup).toBeInTheDocument();
|
|
84
|
+
|
|
85
|
+
/*
|
|
86
|
+
* Landing on the popup's first control is what makes the field
|
|
87
|
+
* fillable: for a colour that control is ChromePicker's hex box, so
|
|
88
|
+
* "open it and type the brand hex" is a complete keyboard path.
|
|
89
|
+
*/
|
|
90
|
+
expect(popup.contains(document.activeElement)).toBe(true);
|
|
91
|
+
},
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
test("a keyboard user can open the picker and set a hex end to end", async () => {
|
|
95
|
+
const onChange: MockFunction = renderColorPicker();
|
|
96
|
+
const field: HTMLInputElement = getField("color-value");
|
|
97
|
+
|
|
98
|
+
field.focus();
|
|
99
|
+
fireEvent.keyDown(field, { key: "Enter" });
|
|
100
|
+
|
|
101
|
+
const focusedControl: HTMLElement = document.activeElement as HTMLElement;
|
|
102
|
+
|
|
103
|
+
expect(focusedControl.tagName).toEqual("INPUT");
|
|
104
|
+
|
|
105
|
+
fireEvent.change(focusedControl, { target: { value: "#32a852" } });
|
|
106
|
+
|
|
107
|
+
await waitFor(() => {
|
|
108
|
+
expect(onChange).toHaveBeenCalledWith(new Color("#32a852"));
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// Escape puts the user back where they were, with the value kept.
|
|
112
|
+
fireEvent.keyDown(focusedControl, { key: "Escape" });
|
|
113
|
+
|
|
114
|
+
expect(screen.queryByTestId("color-picker-popup")).toBeNull();
|
|
115
|
+
expect(document.activeElement).toBe(field);
|
|
116
|
+
expect(field.value).toEqual("#32a852");
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test("an opening key is claimed, so nothing downstream acts on it too", () => {
|
|
120
|
+
renderColorPicker();
|
|
121
|
+
|
|
122
|
+
const field: HTMLInputElement = getField("color-value");
|
|
123
|
+
|
|
124
|
+
/*
|
|
125
|
+
* fireEvent returns false when a cancelable event had preventDefault
|
|
126
|
+
* called on it. That is what stops Input's own onEnterPress from also
|
|
127
|
+
* firing, and what would stop a real <form> around the field from
|
|
128
|
+
* submitting on the way past.
|
|
129
|
+
*/
|
|
130
|
+
expect(fireEvent.keyDown(field, { key: "Enter" })).toBe(false);
|
|
131
|
+
expect(fireEvent.keyDown(field, { key: " " })).toBe(false);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test("Enter on an already open field moves focus in rather than closing it", () => {
|
|
135
|
+
renderColorPicker();
|
|
136
|
+
|
|
137
|
+
const field: HTMLInputElement = getField("color-value");
|
|
138
|
+
|
|
139
|
+
fireEvent.click(field);
|
|
140
|
+
|
|
141
|
+
const popup: HTMLElement = screen.getByTestId("color-picker-popup");
|
|
142
|
+
|
|
143
|
+
// A pointer user's popup opens under their cursor and leaves focus alone.
|
|
144
|
+
expect(popup.contains(document.activeElement)).toBe(false);
|
|
145
|
+
|
|
146
|
+
field.focus();
|
|
147
|
+
fireEvent.keyDown(field, { key: "Enter" });
|
|
148
|
+
|
|
149
|
+
expect(screen.getByTestId("color-picker-popup")).toBeInTheDocument();
|
|
150
|
+
expect(popup.contains(document.activeElement)).toBe(true);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test("keys that are not opening keys are left alone", () => {
|
|
154
|
+
renderColorPicker();
|
|
155
|
+
|
|
156
|
+
const field: HTMLInputElement = getField("color-value");
|
|
157
|
+
|
|
158
|
+
expect(fireEvent.keyDown(field, { key: "Tab" })).toBe(true);
|
|
159
|
+
expect(fireEvent.keyDown(field, { key: "a" })).toBe(true);
|
|
160
|
+
expect(screen.queryByTestId("color-picker-popup")).toBeNull();
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test("a readOnly field opens for neither pointer nor keyboard", () => {
|
|
164
|
+
renderColorPicker({ readOnly: true });
|
|
165
|
+
|
|
166
|
+
const field: HTMLInputElement = getField("color-value");
|
|
167
|
+
|
|
168
|
+
fireEvent.click(field);
|
|
169
|
+
fireEvent.keyDown(field, { key: "Enter" });
|
|
170
|
+
fireEvent.keyDown(field, { key: "ArrowDown" });
|
|
171
|
+
|
|
172
|
+
expect(screen.queryByTestId("color-picker-popup")).toBeNull();
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
test("a disabled field opens for neither pointer nor keyboard", () => {
|
|
176
|
+
renderColorPicker({ disabled: true });
|
|
177
|
+
|
|
178
|
+
const field: HTMLInputElement = getField("color-value");
|
|
179
|
+
|
|
180
|
+
fireEvent.click(field);
|
|
181
|
+
fireEvent.keyDown(field, { key: "Enter" });
|
|
182
|
+
fireEvent.keyDown(field, { key: "ArrowDown" });
|
|
183
|
+
|
|
184
|
+
expect(screen.queryByTestId("color-picker-popup")).toBeNull();
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
test("the field advertises the popup it controls", () => {
|
|
188
|
+
renderColorPicker();
|
|
189
|
+
|
|
190
|
+
const field: HTMLInputElement = getField("color-value");
|
|
191
|
+
|
|
192
|
+
expect(field).toHaveAttribute("aria-haspopup", "dialog");
|
|
193
|
+
expect(field).toHaveAttribute("aria-expanded", "false");
|
|
194
|
+
expect(field).not.toHaveAttribute("aria-controls");
|
|
195
|
+
|
|
196
|
+
fireEvent.click(field);
|
|
197
|
+
|
|
198
|
+
const popup: HTMLElement = screen.getByTestId("color-picker-popup");
|
|
199
|
+
|
|
200
|
+
expect(field).toHaveAttribute("aria-expanded", "true");
|
|
201
|
+
expect(field.getAttribute("aria-controls")).toEqual(popup.id);
|
|
202
|
+
expect(popup.id).toBeTruthy();
|
|
203
|
+
expect(popup).toHaveAttribute("role", "dialog");
|
|
204
|
+
expect(popup).toHaveAttribute("aria-label", "Color picker");
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
describe("IconPicker", () => {
|
|
209
|
+
type RenderIconPickerFunction = () => MockFunction;
|
|
210
|
+
|
|
211
|
+
const renderIconPicker: RenderIconPickerFunction = (): MockFunction => {
|
|
212
|
+
const onChange: MockFunction = getJestMockFunction();
|
|
213
|
+
|
|
214
|
+
render(
|
|
215
|
+
<IconPicker
|
|
216
|
+
dataTestId="icon-value"
|
|
217
|
+
placeholder="No icon"
|
|
218
|
+
onChange={onChange}
|
|
219
|
+
tabIndex={0}
|
|
220
|
+
/>,
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
return onChange;
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
test.each(OPENING_KEYS)(
|
|
227
|
+
"%p opens the popup and moves focus into it",
|
|
228
|
+
(key: string) => {
|
|
229
|
+
renderIconPicker();
|
|
230
|
+
|
|
231
|
+
const field: HTMLInputElement = getField("icon-value");
|
|
232
|
+
|
|
233
|
+
field.focus();
|
|
234
|
+
fireEvent.keyDown(field, { key });
|
|
235
|
+
|
|
236
|
+
const popup: HTMLElement = screen.getByTestId("icon-picker-popup");
|
|
237
|
+
|
|
238
|
+
expect(popup).toBeInTheDocument();
|
|
239
|
+
expect(popup.contains(document.activeElement)).toBe(true);
|
|
240
|
+
},
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
test("a keyboard user can search for an icon and choose it", () => {
|
|
244
|
+
const onChange: MockFunction = renderIconPicker();
|
|
245
|
+
const field: HTMLInputElement = getField("icon-value");
|
|
246
|
+
|
|
247
|
+
field.focus();
|
|
248
|
+
fireEvent.keyDown(field, { key: "Enter" });
|
|
249
|
+
|
|
250
|
+
const popup: HTMLElement = screen.getByTestId("icon-picker-popup");
|
|
251
|
+
const searchBox: HTMLElement = document.activeElement as HTMLElement;
|
|
252
|
+
|
|
253
|
+
expect(popup.contains(searchBox)).toBe(true);
|
|
254
|
+
|
|
255
|
+
fireEvent.change(searchBox, { target: { value: IconProp.Alert } });
|
|
256
|
+
|
|
257
|
+
/*
|
|
258
|
+
* The cells used to be plain divs, so Tab skipped straight past the only
|
|
259
|
+
* control that can set this field.
|
|
260
|
+
*/
|
|
261
|
+
const cell: HTMLElement = within(popup).getByRole("button", {
|
|
262
|
+
name: IconProp.Alert,
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
cell.focus();
|
|
266
|
+
expect(document.activeElement).toBe(cell);
|
|
267
|
+
|
|
268
|
+
fireEvent.click(cell);
|
|
269
|
+
|
|
270
|
+
expect(onChange).toHaveBeenCalledWith(IconProp.Alert);
|
|
271
|
+
expect(screen.queryByTestId("icon-picker-popup")).toBeNull();
|
|
272
|
+
expect(document.activeElement).toBe(field);
|
|
273
|
+
expect(field.value).toEqual(IconProp.Alert);
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
test("the field advertises the popup it controls", () => {
|
|
277
|
+
renderIconPicker();
|
|
278
|
+
|
|
279
|
+
const field: HTMLInputElement = getField("icon-value");
|
|
280
|
+
|
|
281
|
+
expect(field).toHaveAttribute("aria-haspopup", "dialog");
|
|
282
|
+
expect(field).toHaveAttribute("aria-expanded", "false");
|
|
283
|
+
|
|
284
|
+
fireEvent.click(field);
|
|
285
|
+
|
|
286
|
+
const popup: HTMLElement = screen.getByTestId("icon-picker-popup");
|
|
287
|
+
|
|
288
|
+
expect(field).toHaveAttribute("aria-expanded", "true");
|
|
289
|
+
expect(field.getAttribute("aria-controls")).toEqual(popup.id);
|
|
290
|
+
expect(popup).toHaveAttribute("role", "dialog");
|
|
291
|
+
expect(popup).toHaveAttribute("aria-label", "Icon picker");
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
/*
|
|
296
|
+
* The two surfaces the field actually ships on: inside a form, where Enter is
|
|
297
|
+
* a submit, and inside a modal, where Escape is a dismissal.
|
|
298
|
+
*/
|
|
299
|
+
describe("inside the Label form and its modal", () => {
|
|
300
|
+
const LABEL_FIELDS: Fields<FormValues<Record<string, unknown>>> = [
|
|
301
|
+
{
|
|
302
|
+
field: { name: true },
|
|
303
|
+
title: "Name",
|
|
304
|
+
fieldType: FormFieldSchemaType.Text,
|
|
305
|
+
required: true,
|
|
306
|
+
dataTestId: "name",
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
field: { color: true },
|
|
310
|
+
title: "Label Color",
|
|
311
|
+
fieldType: FormFieldSchemaType.Color,
|
|
312
|
+
required: true,
|
|
313
|
+
dataTestId: "color-value",
|
|
314
|
+
},
|
|
315
|
+
];
|
|
316
|
+
|
|
317
|
+
test("Enter on the colour field opens the picker and posts nothing", async () => {
|
|
318
|
+
const onSubmit: MockFunction = getJestMockFunction();
|
|
319
|
+
|
|
320
|
+
render(
|
|
321
|
+
<BasicForm
|
|
322
|
+
id="create-label"
|
|
323
|
+
fields={LABEL_FIELDS}
|
|
324
|
+
onSubmit={onSubmit}
|
|
325
|
+
submitButtonText="Create Label"
|
|
326
|
+
/>,
|
|
327
|
+
);
|
|
328
|
+
|
|
329
|
+
fireEvent.change(screen.getByTestId("name"), {
|
|
330
|
+
target: { value: "WB Unit-BB" },
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
const field: HTMLInputElement = getField("color-value");
|
|
334
|
+
|
|
335
|
+
field.focus();
|
|
336
|
+
expect(fireEvent.keyDown(field, { key: "Enter" })).toBe(false);
|
|
337
|
+
|
|
338
|
+
expect(screen.getByTestId("color-picker-popup")).toBeInTheDocument();
|
|
339
|
+
|
|
340
|
+
/*
|
|
341
|
+
* The picker is the only thing that happens: no submit, so no "Label
|
|
342
|
+
* Color is required." for a field being filled in right now.
|
|
343
|
+
*/
|
|
344
|
+
await waitFor(() => {
|
|
345
|
+
expect(screen.queryByText("Label Color is required.")).toBeNull();
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
expect(onSubmit).not.toHaveBeenCalled();
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
test("Escape closes the picker without taking the modal with it", () => {
|
|
352
|
+
const onClose: MockFunction = getJestMockFunction();
|
|
353
|
+
|
|
354
|
+
render(
|
|
355
|
+
<Modal title="Create New Label" onClose={onClose}>
|
|
356
|
+
<ColorPicker
|
|
357
|
+
dataTestId="color-value"
|
|
358
|
+
placeholder="Please select color for this label."
|
|
359
|
+
onChange={getJestMockFunction()}
|
|
360
|
+
tabIndex={0}
|
|
361
|
+
/>
|
|
362
|
+
</Modal>,
|
|
363
|
+
);
|
|
364
|
+
|
|
365
|
+
const field: HTMLInputElement = getField("color-value");
|
|
366
|
+
|
|
367
|
+
field.focus();
|
|
368
|
+
fireEvent.keyDown(field, { key: "ArrowDown" });
|
|
369
|
+
|
|
370
|
+
expect(screen.getByTestId("color-picker-popup")).toBeInTheDocument();
|
|
371
|
+
|
|
372
|
+
fireEvent.keyDown(document.activeElement as HTMLElement, {
|
|
373
|
+
key: "Escape",
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
expect(screen.queryByTestId("color-picker-popup")).toBeNull();
|
|
377
|
+
expect(onClose).not.toHaveBeenCalled();
|
|
378
|
+
expect(screen.getByTestId("modal")).toBeInTheDocument();
|
|
379
|
+
expect(document.activeElement).toBe(field);
|
|
380
|
+
});
|
|
381
|
+
});
|
|
382
|
+
});
|