@elliemae/ds-app-picker 3.61.11 → 3.70.0-alpha.9
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/DSAppPicker.js +10 -141
- package/dist/cjs/DSAppPicker.js.map +3 -3
- package/dist/cjs/{utils.js → DSAppPickerCTX.js} +8 -25
- package/dist/cjs/DSAppPickerCTX.js.map +7 -0
- package/dist/cjs/config/useAppPicker.js +25 -8
- package/dist/cjs/config/useAppPicker.js.map +3 -3
- package/dist/cjs/config/useFocusTracker.js +104 -0
- package/dist/cjs/config/useFocusTracker.js.map +7 -0
- package/dist/cjs/config/useValidateProps.js +63 -0
- package/dist/cjs/config/useValidateProps.js.map +7 -0
- package/dist/cjs/constants/index.js +11 -1
- package/dist/cjs/constants/index.js.map +2 -2
- package/dist/cjs/index.js +8 -1
- package/dist/cjs/index.js.map +3 -3
- package/dist/cjs/parts/AppPanel.js +143 -0
- package/dist/cjs/parts/AppPanel.js.map +7 -0
- package/dist/cjs/parts/AppPickerFloatingContext/AppPickerContent.js +104 -0
- package/dist/cjs/parts/AppPickerFloatingContext/AppPickerContent.js.map +7 -0
- package/dist/cjs/parts/AppPickerFloatingContext/useAppPickerFloatingContext.js +195 -0
- package/dist/cjs/parts/AppPickerFloatingContext/useAppPickerFloatingContext.js.map +7 -0
- package/dist/cjs/parts/AppPickerItem.js +104 -0
- package/dist/cjs/parts/AppPickerItem.js.map +7 -0
- package/dist/cjs/parts/AppSection.js +95 -0
- package/dist/cjs/parts/AppSection.js.map +7 -0
- package/dist/cjs/parts/Trigger.js +83 -0
- package/dist/cjs/parts/Trigger.js.map +7 -0
- package/dist/cjs/parts/shared-styles.js +44 -0
- package/dist/cjs/parts/shared-styles.js.map +7 -0
- package/dist/cjs/react-desc-prop-types.js +28 -8
- package/dist/cjs/react-desc-prop-types.js.map +2 -2
- package/dist/cjs/typescript-testing/slot-props.js +48 -0
- package/dist/cjs/typescript-testing/slot-props.js.map +7 -0
- package/dist/cjs/typescript-testing/typescript-app-picker-valid.js +15 -2
- package/dist/cjs/typescript-testing/typescript-app-picker-valid.js.map +2 -2
- package/dist/cjs/util/getChipId.js +36 -0
- package/dist/cjs/util/getChipId.js.map +7 -0
- package/dist/cjs/util/getChipRegionId.js +37 -0
- package/dist/cjs/util/getChipRegionId.js.map +7 -0
- package/dist/cjs/util/getDSAppPickerContractProps.js +38 -0
- package/dist/cjs/util/getDSAppPickerContractProps.js.map +7 -0
- package/dist/cjs/util/instanceIds.js +42 -0
- package/dist/cjs/util/instanceIds.js.map +7 -0
- package/dist/esm/DSAppPicker.js +10 -141
- package/dist/esm/DSAppPicker.js.map +3 -3
- package/dist/esm/DSAppPickerCTX.js +8 -0
- package/dist/esm/DSAppPickerCTX.js.map +7 -0
- package/dist/esm/config/useAppPicker.js +29 -13
- package/dist/esm/config/useAppPicker.js.map +3 -3
- package/dist/esm/config/useFocusTracker.js +74 -0
- package/dist/esm/config/useFocusTracker.js.map +7 -0
- package/dist/esm/config/useValidateProps.js +33 -0
- package/dist/esm/config/useValidateProps.js.map +7 -0
- package/dist/esm/constants/index.js +11 -1
- package/dist/esm/constants/index.js.map +2 -2
- package/dist/esm/index.js +13 -1
- package/dist/esm/index.js.map +3 -3
- package/dist/esm/parts/AppPanel.js +118 -0
- package/dist/esm/parts/AppPanel.js.map +7 -0
- package/dist/esm/parts/AppPickerFloatingContext/AppPickerContent.js +74 -0
- package/dist/esm/parts/AppPickerFloatingContext/AppPickerContent.js.map +7 -0
- package/dist/esm/parts/AppPickerFloatingContext/useAppPickerFloatingContext.js +165 -0
- package/dist/esm/parts/AppPickerFloatingContext/useAppPickerFloatingContext.js.map +7 -0
- package/dist/esm/parts/AppPickerItem.js +74 -0
- package/dist/esm/parts/AppPickerItem.js.map +7 -0
- package/dist/esm/parts/AppSection.js +65 -0
- package/dist/esm/parts/AppSection.js.map +7 -0
- package/dist/esm/parts/Trigger.js +53 -0
- package/dist/esm/parts/Trigger.js.map +7 -0
- package/dist/esm/parts/shared-styles.js +14 -0
- package/dist/esm/parts/shared-styles.js.map +7 -0
- package/dist/esm/react-desc-prop-types.js +34 -9
- package/dist/esm/react-desc-prop-types.js.map +2 -2
- package/dist/esm/typescript-testing/slot-props.js +25 -0
- package/dist/esm/typescript-testing/slot-props.js.map +7 -0
- package/dist/esm/typescript-testing/typescript-app-picker-valid.js +15 -2
- package/dist/esm/typescript-testing/typescript-app-picker-valid.js.map +2 -2
- package/dist/esm/util/getChipId.js +6 -0
- package/dist/esm/util/getChipId.js.map +7 -0
- package/dist/esm/util/getChipRegionId.js +7 -0
- package/dist/esm/util/getChipRegionId.js.map +7 -0
- package/dist/esm/util/getDSAppPickerContractProps.js +8 -0
- package/dist/esm/util/getDSAppPickerContractProps.js.map +7 -0
- package/dist/esm/util/instanceIds.js +12 -0
- package/dist/esm/util/instanceIds.js.map +7 -0
- package/dist/types/DSAppPicker.d.ts +1 -2
- package/dist/types/DSAppPickerCTX.d.ts +6 -0
- package/dist/types/config/useAppPicker.d.ts +111 -92
- package/dist/types/config/useFocusTracker.d.ts +19 -0
- package/dist/types/config/useValidateProps.d.ts +3 -0
- package/dist/types/constants/index.d.ts +17 -7
- package/dist/types/index.d.ts +2 -0
- package/dist/types/parts/AppPanel.d.ts +2 -0
- package/dist/types/parts/AppPickerFloatingContext/AppPickerContent.d.ts +3 -0
- package/dist/types/parts/AppPickerFloatingContext/useAppPickerFloatingContext.d.ts +171 -0
- package/dist/types/parts/AppPickerItem.d.ts +11 -0
- package/dist/types/parts/AppSection.d.ts +6 -0
- package/dist/types/parts/Trigger.d.ts +14 -0
- package/dist/types/parts/shared-styles.d.ts +2 -0
- package/dist/types/react-desc-prop-types.d.ts +112 -9
- package/dist/types/util/getChipId.d.ts +2 -0
- package/dist/types/util/getChipRegionId.d.ts +1 -0
- package/dist/types/util/getDSAppPickerContractProps.d.ts +37 -0
- package/dist/types/util/instanceIds.d.ts +11 -0
- package/package.json +21 -18
- package/dist/cjs/AppPickerImpl.js +0 -204
- package/dist/cjs/AppPickerImpl.js.map +0 -7
- package/dist/cjs/hooks/useKeepTrackButtons.js +0 -69
- package/dist/cjs/hooks/useKeepTrackButtons.js.map +0 -7
- package/dist/cjs/styles.js +0 -76
- package/dist/cjs/styles.js.map +0 -7
- package/dist/cjs/utils.js.map +0 -7
- package/dist/esm/AppPickerImpl.js +0 -174
- package/dist/esm/AppPickerImpl.js.map +0 -7
- package/dist/esm/hooks/useKeepTrackButtons.js +0 -39
- package/dist/esm/hooks/useKeepTrackButtons.js.map +0 -7
- package/dist/esm/styles.js +0 -46
- package/dist/esm/styles.js.map +0 -7
- package/dist/esm/utils.js +0 -25
- package/dist/esm/utils.js.map +0 -7
- package/dist/types/AppPickerImpl.d.ts +0 -11
- package/dist/types/hooks/useKeepTrackButtons.d.ts +0 -13
- package/dist/types/styles.d.ts +0 -7
- package/dist/types/tests/app-picker.a11y.test.d.ts +0 -1
- package/dist/types/tests/app-picker.data-testid.test.d.ts +0 -1
- package/dist/types/tests/app-picker.events.test.d.ts +0 -1
- package/dist/types/tests/app-picker.exports.test.d.ts +0 -1
- package/dist/types/tests/app-picker.get-owner-props.test.d.ts +0 -1
- package/dist/types/tests/app-picker.keyboard.test.d.ts +0 -1
- package/dist/types/typescript-testing/typescript-app-picker-valid.d.ts +0 -1
- package/dist/types/utils.d.ts +0 -14
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import type { useAppPicker } from '../../config/useAppPicker.js';
|
|
3
|
+
import type { DSAppPickerT } from '../../react-desc-prop-types.js';
|
|
4
|
+
/**
|
|
5
|
+
* useAppPickerFloatingContext — the single point that wires DSAppPicker to
|
|
6
|
+
* `@elliemae/ds-floating-context`'s `useFloatingContext`.
|
|
7
|
+
*
|
|
8
|
+
* WHAT IT DOES (as a black box):
|
|
9
|
+
* AppPicker can be used in two very different ways:
|
|
10
|
+
* - Controlled: the consumer supplies `isOpen` and drives the open/close transition.
|
|
11
|
+
* └→ AppPicker can't be event-based as the flip might originate from any cause and we don't own the business logic
|
|
12
|
+
* └→ we need to ensure WCAG embedded behaviours (focus, Escape, click-outside) are wired to the consumer's `isOpen` flip.
|
|
13
|
+
* - Uncontrolled: the consumer does not supply `isOpen` and AppPicker owns the full flows, we only notify the consumer of the transition and its cause.
|
|
14
|
+
*
|
|
15
|
+
* The AppPicker and this file in particular serves as the one place that coordinates the two different ways to use the component and wires with the useFloatingContext hook.
|
|
16
|
+
* This files owns:
|
|
17
|
+
* - Focus management relative to open/close transitions (initial focus on open, return focus to trigger on close)
|
|
18
|
+
* - Escape key handling (scoped to the panel and trigger)
|
|
19
|
+
* - Click-outside handling (panel and trigger)
|
|
20
|
+
* - Imperative focus API via actionRef
|
|
21
|
+
* - Controlled/Uncontrolled forks and differences in event notification (eager vs controlled-flip)
|
|
22
|
+
* - OnOpen and OnClose notification to the consumer with the correct reason and event (if any)
|
|
23
|
+
*
|
|
24
|
+
* IMPORTANT:
|
|
25
|
+
* Parts of the flow/logic required in this file is offloaded to useFloatingContext internal assumptions and logics.
|
|
26
|
+
* Changes to useFloatingContext may break the flows in this file, or alter them in ways that are not obvious, the two are closely coupled.
|
|
27
|
+
*
|
|
28
|
+
* DATA / EVENT FLOW
|
|
29
|
+
*
|
|
30
|
+
* Two concerns run in parallel and must not be conflated: (A) what makes the panel show/hide and take
|
|
31
|
+
* focus, and (B) how the consumer is told it opened/closed. They have different origins.
|
|
32
|
+
*
|
|
33
|
+
* (A) PANEL VISIBILITY + FOCUS — useFloatingContext internal co-ownership of the flow.
|
|
34
|
+
*
|
|
35
|
+
* a user action │ │ isOpen prop flip
|
|
36
|
+
* (trigger click / scoped Escape / click outside) │ │ (not a traceable user action, but the consumer's own flip)
|
|
37
|
+
* uncontrolled - flips the internal flag │ │ controlled - AppPicker's own flip is inert, consumer drives it
|
|
38
|
+
* │
|
|
39
|
+
* ▼
|
|
40
|
+
* resolvedIsOpen changes
|
|
41
|
+
* │
|
|
42
|
+
* ▼
|
|
43
|
+
* useFloatingContext (externallyControlledIsOpen) detects the transition
|
|
44
|
+
* │
|
|
45
|
+
* ▼
|
|
46
|
+
* on open │ │ on close
|
|
47
|
+
* handleFloatingOpen │ │ focus is only restored after a SCOPED ESCAPE:
|
|
48
|
+
* AppPicker business logic seeds initial focus │ │ useFloatingContext returns focus to the trigger
|
|
49
|
+
* │ │ (returnFocusToReference). Click-outside / toggle
|
|
50
|
+
* │ │ / controlled closes do NOT move focus, and
|
|
51
|
+
* │ │ handleFloatingClose only NOTIFIES (see (B)).
|
|
52
|
+
* │
|
|
53
|
+
* ▼
|
|
54
|
+
* ┌─────────────────────────────────────────────────────────────┐
|
|
55
|
+
* │ visibility + focus are delivered to final user as expected │
|
|
56
|
+
* └─────────────────────────────────────────────────────────────┘
|
|
57
|
+
*
|
|
58
|
+
* (B) CONSUMER NOTIFICATION
|
|
59
|
+
* Each flow below is the UNCONTROLLED path, where AppPicker owns the transition and can name the true cause.
|
|
60
|
+
* The controlled equivalent for every trigger is stated once at the end.
|
|
61
|
+
* ┌────────────────────────┐
|
|
62
|
+
* │ OPENING TRIGGERS FLOWS │
|
|
63
|
+
* └────────────────────────┘
|
|
64
|
+
* TRIGGER CLICK (uncontrolled)
|
|
65
|
+
* trigger click
|
|
66
|
+
* user clicks the internal trigger, panel closed
|
|
67
|
+
* │
|
|
68
|
+
* ▼
|
|
69
|
+
* handleTriggerClick
|
|
70
|
+
* ( flips the flag and wires the visual and focus Flow (A) above )
|
|
71
|
+
* │
|
|
72
|
+
* ▼
|
|
73
|
+
* ┌──────────────────────────────────────────────┐
|
|
74
|
+
* │ onOpen { reason: 'trigger', event } │ fired EAGERLY, when the event is triggered
|
|
75
|
+
* └──────────────────────────────────────────────┘
|
|
76
|
+
* ------------------------------------------------------------------------------------------------------------------------
|
|
77
|
+
* ┌────────────────────────┐
|
|
78
|
+
* │ CLOSING TRIGGERS FLOWS │
|
|
79
|
+
* └────────────────────────┘
|
|
80
|
+
* KEYBOARD ESCAPE (uncontrolled)
|
|
81
|
+
* Escape within the panel DOM tree or the trigger
|
|
82
|
+
* scoped Escape (routed only while closeOnEscape, i.e. no consumer onKeyDown)
|
|
83
|
+
* │
|
|
84
|
+
* ▼
|
|
85
|
+
* onEscape → handleScopedEscape
|
|
86
|
+
* ( flips the flag and wires the visual and focus Flow (A) above )
|
|
87
|
+
* │
|
|
88
|
+
* ▼
|
|
89
|
+
* ┌──────────────────────────────────────────────┐
|
|
90
|
+
* │ onClose { reason: 'escape', event } │ fired EAGERLY, when the event is triggered
|
|
91
|
+
* └──────────────────────────────────────────────┘
|
|
92
|
+
*
|
|
93
|
+
* CLICK OUTSIDE (uncontrolled)
|
|
94
|
+
* Click outside the panel DOM tree and the trigger
|
|
95
|
+
* useFloatingContext detects a pointer press outside the panel + trigger
|
|
96
|
+
* │
|
|
97
|
+
* ▼
|
|
98
|
+
* handleClickOutside
|
|
99
|
+
* ( flips the flag and wires the visual and focus Flow (A) above )
|
|
100
|
+
* │
|
|
101
|
+
* ▼
|
|
102
|
+
* ┌──────────────────────────────────────────────┐
|
|
103
|
+
* │ onClose { reason: 'click-outside', event } │ fired EAGERLY, when the event is triggered
|
|
104
|
+
* └──────────────────────────────────────────────┘
|
|
105
|
+
*
|
|
106
|
+
* CLICK ON TRIGGER (uncontrolled, while internal state is open)
|
|
107
|
+
* trigger click while open
|
|
108
|
+
* user clicks the internal trigger, panel open
|
|
109
|
+
* │
|
|
110
|
+
* ▼
|
|
111
|
+
* handleTriggerClick
|
|
112
|
+
* ( flips the flag and wires the visual and focus Flow (A) above )
|
|
113
|
+
* │
|
|
114
|
+
* ▼
|
|
115
|
+
* ┌──────────────────────────────────────────────┐
|
|
116
|
+
* │ onClose { reason: 'trigger-toggle', event } │ fired EAGERLY, when the event is triggered
|
|
117
|
+
* └──────────────────────────────────────────────┘
|
|
118
|
+
* ------------------------------------------------------------------------------------------------------------------------
|
|
119
|
+
* ┌──────────────────────────────────┐
|
|
120
|
+
* │ CONTROLLED OPEN & CLOSE FLOWS │
|
|
121
|
+
* └──────────────────────────────────┘
|
|
122
|
+
* AppPicker's own flip is inert;
|
|
123
|
+
* We can't fire EAGERLY and we can't track an originating event, there may be NONE
|
|
124
|
+
* The consumer's `isOpen` flip is reacted to by useFloatingContext, which fires onOpen / onClose with reason 'controlled-flip'
|
|
125
|
+
* We wire the visual and focus Flow (A) above, and then notify the consumer with reason 'controlled' (no event)
|
|
126
|
+
* │
|
|
127
|
+
* ▼
|
|
128
|
+
* ┌──────────────────────────────────────────────┐
|
|
129
|
+
* │ onOpen { reason: 'controlled' } │ no event
|
|
130
|
+
* │ onClose { reason: 'controlled' } │ a prop flip is not a traceable user action
|
|
131
|
+
* └──────────────────────────────────────────────┘
|
|
132
|
+
* ------------------------------------------------------------------------------------------------------------------------
|
|
133
|
+
*
|
|
134
|
+
* Eager (uncontrolled) and controlled-flip paths are mutually exclusive on `isControlled`.
|
|
135
|
+
* the consumer is notified exactly once per transition — never twice.
|
|
136
|
+
* On Escape specifically
|
|
137
|
+
* closeOnEscape is false whenever the picker is controlled (and whenever a consumer supplies onKeyDown)
|
|
138
|
+
* └→ AppPicker's onEscape never runs —
|
|
139
|
+
* └→ the close arrives purely by listening to the flag transition
|
|
140
|
+
* ------------------------------------------------------------------------------------------------------------------------
|
|
141
|
+
* A nuance for an edge case:
|
|
142
|
+
*
|
|
143
|
+
* Declarative mount-open (rendered already-open).
|
|
144
|
+
* useFloatingContext fires no onOpen for it (it did not transition to open, it started open),
|
|
145
|
+
* └→ neither the focus-seed (A) nor the notification (B) runs
|
|
146
|
+
* AppPanel compensates this locally via useMountedOpenAutofocusNuance, it needs to be seen if this is even desired or should be removed
|
|
147
|
+
* ------------------------------------------------------------------------------------------------------------------------
|
|
148
|
+
*/
|
|
149
|
+
type ConfiguredAppPickerCtx = ReturnType<typeof useAppPicker>;
|
|
150
|
+
type AppPickerFloatingContextConfig = {
|
|
151
|
+
propsWithDefault: DSAppPickerT.InternalProps;
|
|
152
|
+
focusTrackers: ConfiguredAppPickerCtx['focusTrackers'];
|
|
153
|
+
openIntentRef: ConfiguredAppPickerCtx['openIntentRef'];
|
|
154
|
+
resolvedIsOpen: ConfiguredAppPickerCtx['resolvedIsOpen'];
|
|
155
|
+
setInternalIsOpen: ConfiguredAppPickerCtx['setInternalIsOpen'];
|
|
156
|
+
};
|
|
157
|
+
export declare const useAppPickerFloatingContext: (appPickerConfig: AppPickerFloatingContextConfig) => {
|
|
158
|
+
floatingStyles: React.CSSProperties;
|
|
159
|
+
arrowStyles: import("@elliemae/ds-floating-context/dist/types/parts/PopoverArrow.js").PopoverArrowT;
|
|
160
|
+
floatingContext: {
|
|
161
|
+
withoutPortal: boolean;
|
|
162
|
+
withoutAnimation: boolean;
|
|
163
|
+
portalDOMContainer: HTMLElement | undefined;
|
|
164
|
+
animationDuration: number;
|
|
165
|
+
};
|
|
166
|
+
handleTriggerRef: (instance: HTMLButtonElement | null) => void | React.DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES[keyof React.DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES];
|
|
167
|
+
handleTriggerClick: (e: React.MouseEvent | React.KeyboardEvent) => void;
|
|
168
|
+
handleTriggerKeyDown: (e: React.KeyboardEvent) => void;
|
|
169
|
+
floatingInnerRef: (instance: HTMLDivElement | null) => void | React.DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES[keyof React.DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES];
|
|
170
|
+
};
|
|
171
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { DSAppPickerT } from '../react-desc-prop-types.js';
|
|
3
|
+
interface AppPickerItemProps {
|
|
4
|
+
app: DSAppPickerT.AppItem;
|
|
5
|
+
chipId: string;
|
|
6
|
+
isFirstFocusable: boolean;
|
|
7
|
+
isLastFocusable: boolean;
|
|
8
|
+
positionAnnouncement: string;
|
|
9
|
+
}
|
|
10
|
+
export declare const AppPickerItem: React.FC<AppPickerItemProps>;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { useAppPickerFloatingContext } from './AppPickerFloatingContext/useAppPickerFloatingContext.js';
|
|
3
|
+
type UseAppPickerFloatingContextT = ReturnType<typeof useAppPickerFloatingContext>;
|
|
4
|
+
type FloatingContextDictatedProps = {
|
|
5
|
+
handleTriggerRef: UseAppPickerFloatingContextT['handleTriggerRef'];
|
|
6
|
+
handleTriggerClick: UseAppPickerFloatingContextT['handleTriggerClick'];
|
|
7
|
+
handleTriggerKeyDown: UseAppPickerFloatingContextT['handleTriggerKeyDown'];
|
|
8
|
+
dialogLabelId: string;
|
|
9
|
+
};
|
|
10
|
+
export declare const Trigger: {
|
|
11
|
+
(PropsFromFloatingContext: FloatingContextDictatedProps): React.JSX.Element | null;
|
|
12
|
+
displayName: string;
|
|
13
|
+
};
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export declare const StyledListItemFullRow: import("styled-components").StyledComponent<"div", import("@elliemae/ds-system").Theme, object & import("@elliemae/ds-system").OwnerInterface & import("@elliemae/ds-system").InnerRefInterface<"div">, never>;
|
|
2
|
+
export declare const StyledSection: import("styled-components").StyledComponent<"div", import("@elliemae/ds-system").Theme, object & import("@elliemae/ds-system").OwnerInterface & import("@elliemae/ds-system").InnerRefInterface<"div">, never>;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { SvgIconT } from '@elliemae/ds-icons';
|
|
3
|
-
import type { GlobalAttributesT, XstyledProps, ValidationMap } from '@elliemae/ds-props-helpers';
|
|
3
|
+
import type { GlobalAttributesT, XstyledProps, DSPropTypesSchema, ValidationMap } from '@elliemae/ds-props-helpers';
|
|
4
4
|
import type { TypescriptHelpersT } from '@elliemae/ds-typescript-helpers';
|
|
5
5
|
import { DSAppPickerName, DSAppPickerSlots } from './constants/index.js';
|
|
6
6
|
export declare namespace DSAppPickerT {
|
|
@@ -22,31 +22,134 @@ export declare namespace DSAppPickerT {
|
|
|
22
22
|
focusSelectedOrFirstAvailable?: () => void;
|
|
23
23
|
focusWrapper: () => void;
|
|
24
24
|
}>;
|
|
25
|
+
/**
|
|
26
|
+
* Why the panel opened. `trigger` carries the activating event and fires synchronously in the
|
|
27
|
+
* trigger's click handler (AppPicker owns the open). `controlled` fires when the consumer flips
|
|
28
|
+
* the `isOpen` prop themselves — no event, delivered when that flip takes effect.
|
|
29
|
+
*/
|
|
30
|
+
type OpenChange = {
|
|
31
|
+
reason: 'trigger';
|
|
32
|
+
event: React.MouseEvent | React.KeyboardEvent;
|
|
33
|
+
} | {
|
|
34
|
+
reason: 'controlled';
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* Why the panel closed. The event-carrying reasons fire synchronously from the handler that owns
|
|
38
|
+
* the close (AppPicker in uncontrolled mode): `escape` (a scoped Escape), `click-outside`, and
|
|
39
|
+
* `trigger-toggle` (the trigger clicked while open). `controlled` fires when the consumer flips the
|
|
40
|
+
* `isOpen` prop themselves — no event, delivered when that flip takes effect.
|
|
41
|
+
*/
|
|
42
|
+
type CloseChange = {
|
|
43
|
+
reason: 'escape';
|
|
44
|
+
event: KeyboardEvent;
|
|
45
|
+
} | {
|
|
46
|
+
reason: 'click-outside';
|
|
47
|
+
event: MouseEvent | TouchEvent;
|
|
48
|
+
} | {
|
|
49
|
+
reason: 'trigger-toggle';
|
|
50
|
+
event: React.MouseEvent | React.KeyboardEvent;
|
|
51
|
+
} | {
|
|
52
|
+
reason: 'controlled';
|
|
53
|
+
};
|
|
54
|
+
interface RequiredProps {
|
|
55
|
+
}
|
|
56
|
+
type SlotFunctionArguments = {
|
|
57
|
+
dsApppickerRoot: () => object;
|
|
58
|
+
dsApppickerGroup: () => object;
|
|
59
|
+
dsApppickerItem: () => object;
|
|
60
|
+
dsApppickerTitle: () => object;
|
|
61
|
+
dsApppickerSeparator: () => object;
|
|
62
|
+
dsApppickerRow: () => object;
|
|
63
|
+
dsApppickerChip: () => object;
|
|
64
|
+
dsApppickerButton: () => object;
|
|
65
|
+
dsApppickerFloatingWrapper: () => object;
|
|
66
|
+
};
|
|
25
67
|
interface DefaultProps {
|
|
26
68
|
apps: AppItem[];
|
|
27
69
|
customApps: AppItem[];
|
|
28
70
|
sectionTitle: string;
|
|
29
71
|
customSectionTitle: string;
|
|
30
72
|
icon: React.ComponentType<SvgIconT.Props>;
|
|
73
|
+
/**
|
|
74
|
+
* Opt in to arrow-key opening on the trigger: ArrowDown opens and focuses the selected application
|
|
75
|
+
* (the first available one when nothing is selected), ArrowUp opens and focuses the selected
|
|
76
|
+
* application (the last available one when nothing is selected).
|
|
77
|
+
*
|
|
78
|
+
* The picker owns this flow end to end — a consumer does not wire a key handler, drive `isOpen`, or
|
|
79
|
+
* reach through `actionRef` to place the focus. Defaults to `false`, so it is purely additive: a
|
|
80
|
+
* picker that does not opt in behaves exactly as before, with the arrow keys doing nothing on the
|
|
81
|
+
* trigger.
|
|
82
|
+
*
|
|
83
|
+
* Independent of `actionRef`, which stays the imperative handle for a consumer to move focus from
|
|
84
|
+
* their own business logic at any time.
|
|
85
|
+
*/
|
|
86
|
+
openAndFocusOnArrowKeys: boolean;
|
|
31
87
|
}
|
|
32
88
|
interface RenderTriggerProp {
|
|
33
|
-
ref: React.
|
|
89
|
+
ref: React.RefCallback<HTMLButtonElement>;
|
|
34
90
|
[key: string]: unknown;
|
|
35
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* Props handed to a `TriggerComponent`. This is the only trigger API that participates in the
|
|
94
|
+
* dialog's accessible-name wiring: `id` is the element the panel's `aria-labelledby` points at,
|
|
95
|
+
* so a TriggerComponent that drops it leaves the dialog unnamed.
|
|
96
|
+
*
|
|
97
|
+
* `innerRef`, not `ref`, deliberately: under React 18 a `ref` passed through JSX is consumed by
|
|
98
|
+
* the fiber and never reaches a plain function component's props unless it is wrapped in
|
|
99
|
+
* `forwardRef`. `innerRef` is the established Dimsum convention (see `ds-system`'s styled
|
|
100
|
+
* components) and keeps `forwardRef` optional for the consumer — which is the same ergonomic
|
|
101
|
+
* concern that produced DEV-002, solved here without the plain-function call.
|
|
102
|
+
*/
|
|
103
|
+
interface TriggerComponentProps {
|
|
104
|
+
innerRef: React.RefCallback<HTMLButtonElement>;
|
|
105
|
+
id: string;
|
|
106
|
+
onClick: (e: React.MouseEvent | React.KeyboardEvent) => void;
|
|
107
|
+
/**
|
|
108
|
+
* Attach to the trigger element alongside `onClick`. It is what implements
|
|
109
|
+
* `openAndFocusOnArrowKeys`, and it is a keydown handler in its own right — never route a keydown
|
|
110
|
+
* through `onClick` to get the same effect: `onClick` infers keyboard activation from the click's
|
|
111
|
+
* `detail`, which is a property of clicks, not of key presses.
|
|
112
|
+
*
|
|
113
|
+
* Harmless to attach when `openAndFocusOnArrowKeys` is false — it ignores every key in that case.
|
|
114
|
+
*
|
|
115
|
+
* The keys it acts on are ArrowDown and ArrowUp. A trigger that drives its own `isOpen` — which makes
|
|
116
|
+
* the picker's internal flip inert — has to open on those same two keys itself.
|
|
117
|
+
*/
|
|
118
|
+
onKeyDown: (e: React.KeyboardEvent) => void;
|
|
119
|
+
'aria-haspopup': 'dialog';
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* How the pending open was triggered, which is the only input to where initial focus lands:
|
|
123
|
+
* `pointer` → the panel · `keyboard-first` → the selection, else the first available item ·
|
|
124
|
+
* `keyboard-last` → the selection, else the last available item.
|
|
125
|
+
*/
|
|
126
|
+
type OpenIntent = 'pointer' | 'keyboard-first' | 'keyboard-last';
|
|
36
127
|
interface OptionalProps extends TypescriptHelpersT.PropsForGlobalOnSlots<typeof DSAppPickerName, typeof DSAppPickerSlots> {
|
|
37
|
-
|
|
128
|
+
onOpen?: (info: OpenChange) => void;
|
|
129
|
+
onClose?: (info: CloseChange) => void;
|
|
38
130
|
onClick?: (e: React.MouseEvent | React.KeyboardEvent) => void;
|
|
39
|
-
onClickOutside?: (e: React.MouseEvent) => void;
|
|
131
|
+
onClickOutside?: (e: MouseEvent | React.MouseEvent) => void;
|
|
40
132
|
onKeyDown?: (e: React.KeyboardEvent) => void;
|
|
41
133
|
actionRef?: ActionRef;
|
|
42
|
-
|
|
134
|
+
/**
|
|
135
|
+
* @deprecated v4.x — use `TriggerComponent`. `renderTrigger` receives only `ref` and cannot be
|
|
136
|
+
* given the id the dialog's `aria-labelledby` points at, so the panel is left without an
|
|
137
|
+
* accessible name. See DEV-002 in KNOWN_INTENTIONAL_DEVIATIONS.md.
|
|
138
|
+
*/
|
|
139
|
+
renderTrigger?: (props: RenderTriggerProp) => React.ReactElement | null;
|
|
140
|
+
/**
|
|
141
|
+
* Custom trigger, rendered as a real React element so it owns its own fiber. Takes precedence
|
|
142
|
+
* over `renderTrigger` when both are supplied.
|
|
143
|
+
*/
|
|
144
|
+
TriggerComponent?: React.ComponentType<TriggerComponentProps>;
|
|
43
145
|
isOpen?: boolean;
|
|
44
146
|
triggerRef?: React.RefObject<HTMLButtonElement>;
|
|
45
147
|
}
|
|
46
|
-
interface Props extends Partial<DefaultProps>, OptionalProps, Omit<GlobalAttributesT<HTMLDivElement>, keyof DefaultProps | keyof OptionalProps | keyof XstyledProps>, XstyledProps {
|
|
148
|
+
interface Props extends Partial<DefaultProps>, OptionalProps, Omit<GlobalAttributesT<HTMLDivElement>, keyof DefaultProps | keyof OptionalProps | keyof XstyledProps>, XstyledProps, RequiredProps {
|
|
47
149
|
}
|
|
48
|
-
interface InternalProps extends DefaultProps, OptionalProps, Omit<GlobalAttributesT<HTMLDivElement>, keyof DefaultProps | keyof OptionalProps | keyof XstyledProps>, XstyledProps {
|
|
150
|
+
interface InternalProps extends DefaultProps, OptionalProps, Omit<GlobalAttributesT<HTMLDivElement>, keyof DefaultProps | keyof OptionalProps | keyof XstyledProps>, XstyledProps, RequiredProps {
|
|
49
151
|
}
|
|
50
152
|
}
|
|
51
|
-
export declare const defaultProps:
|
|
52
|
-
export declare const DSAppPickerPropTypes:
|
|
153
|
+
export declare const defaultProps: DSAppPickerT.DefaultProps;
|
|
154
|
+
export declare const DSAppPickerPropTypes: DSPropTypesSchema<DSAppPickerT.Props>;
|
|
155
|
+
export declare const DSAppPickerPropTypesSchema: ValidationMap<DSAppPickerT.Props>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const getChipRegionId: (chipId: string) => string;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type DSAppPickerT } from '../react-desc-prop-types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Filters an arbitrary object down to the props DSAppPicker actually accepts.
|
|
4
|
+
*
|
|
5
|
+
* **Who this is for.** Components that wrap DSAppPicker and receive its props indirectly — as a
|
|
6
|
+
* configuration object from their own consumer rather than as JSX props.
|
|
7
|
+
*
|
|
8
|
+
* The canonical case is `ds-global-header`, whose `toolbar` config carries a `componentProps` object for
|
|
9
|
+
* the `ds-app-picker` item type. Such a wrapper cannot spread that object blindly: its config type is
|
|
10
|
+
* DSAppPicker's props *plus* the wrapper's own — `triggerOnClick` there — and those must not leak into
|
|
11
|
+
* DSAppPicker.
|
|
12
|
+
*
|
|
13
|
+
* Do not swap this for a hand-listed set of forwardable keys. A hand-list has no way to fail when the
|
|
14
|
+
* contract grows: the new prop type-checks at the call site, is dropped silently at runtime, and nothing
|
|
15
|
+
* reports it. Deriving the set from the contract is what makes that class of bug impossible.
|
|
16
|
+
*
|
|
17
|
+
* **Why a utility instead of exporting the schema.** `DSAppPickerPropTypes` is internal. It exists to
|
|
18
|
+
* drive prop validation and the Storybook props table; that it also happens to describe the exact set
|
|
19
|
+
* of forwardable props is a convenience today, not a promise. Exposing the schema would weld those
|
|
20
|
+
* concerns together and make every future change to it a public API change. Exposing this utility
|
|
21
|
+
* instead means the public commitment is only "you get the props DSAppPicker accepts" — if the two
|
|
22
|
+
* ever need to diverge, only this function changes.
|
|
23
|
+
*
|
|
24
|
+
* Anything the contract does not declare is discarded. Arbitrary `data-*` attributes are kept (a
|
|
25
|
+
* supported consumer surface no schema can enumerate).
|
|
26
|
+
*
|
|
27
|
+
* `undefined` vs `null` — see `getContractProps` for the implemented rule and the React precedent it
|
|
28
|
+
* follows. In short: `undefined` is dropped ("I am not defining this, you decide" — the wrapper's
|
|
29
|
+
* default stands), `null` is forwarded ("I am explicitly asking for nothing") and is then judged by
|
|
30
|
+
* DSAppPicker's own strictly-typed contract, which rejects it for props whose declared type does not
|
|
31
|
+
* admit null. To disable a handler, pass an explicit no-op (`() => {}`) — it honours the type and
|
|
32
|
+
* states the intent unambiguously.
|
|
33
|
+
*
|
|
34
|
+
* @param props - the object to filter, typically a wrapper's `componentProps` configuration
|
|
35
|
+
* @returns a new object containing only props DSAppPicker declares
|
|
36
|
+
*/
|
|
37
|
+
export declare const getDSAppPickerContractProps: (props: object) => Partial<DSAppPickerT.Props>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** The element the consumer's own `id` lands on, unchanged. */
|
|
2
|
+
export declare const getIdForAppPickerRoot: (id: string) => string;
|
|
3
|
+
/**
|
|
4
|
+
* The trigger button. This is the id the panel's `aria-labelledby` points at, so it is also the answer to
|
|
5
|
+
* "what names the dialog" — see `TriggerComponent` in the Custom Triggers documentation.
|
|
6
|
+
*/
|
|
7
|
+
export declare const getIdForAppPickerTrigger: (id: string) => string;
|
|
8
|
+
/** The `sectionTitle` heading, which labels the `apps` group. */
|
|
9
|
+
export declare const getIdForAppPickerSectionTitle: (id: string) => string;
|
|
10
|
+
/** The `customSectionTitle` heading, which labels the `customApps` group. */
|
|
11
|
+
export declare const getIdForAppPickerCustomSectionTitle: (id: string) => string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elliemae/ds-app-picker",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.70.0-alpha.9",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "ICE MT - Dimsum - App Picker",
|
|
6
6
|
"files": [
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
],
|
|
23
23
|
"repository": {
|
|
24
24
|
"type": "git",
|
|
25
|
-
"url": "https://
|
|
25
|
+
"url": "https://github.com/intcx/PLATFORM-UI.dimsum.git"
|
|
26
26
|
},
|
|
27
27
|
"engines": {
|
|
28
28
|
"pnpm": ">=9",
|
|
@@ -36,35 +36,38 @@
|
|
|
36
36
|
"indent": 4
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"
|
|
40
|
-
"@elliemae/ds-button-v2": "3.
|
|
41
|
-
"@elliemae/ds-
|
|
42
|
-
"@elliemae/ds-chip": "3.
|
|
43
|
-
"@elliemae/ds-grid": "3.
|
|
44
|
-
"@elliemae/ds-
|
|
45
|
-
"@elliemae/ds-
|
|
46
|
-
"@elliemae/ds-
|
|
47
|
-
"@elliemae/ds-
|
|
39
|
+
"uid": "^2.0.2",
|
|
40
|
+
"@elliemae/ds-button-v2": "3.70.0-alpha.9",
|
|
41
|
+
"@elliemae/ds-floating-context": "3.70.0-alpha.9",
|
|
42
|
+
"@elliemae/ds-chip": "3.70.0-alpha.9",
|
|
43
|
+
"@elliemae/ds-grid": "3.70.0-alpha.9",
|
|
44
|
+
"@elliemae/ds-hooks-focus-trap": "3.70.0-alpha.9",
|
|
45
|
+
"@elliemae/ds-icons": "3.70.0-alpha.9",
|
|
46
|
+
"@elliemae/ds-props-helpers": "3.70.0-alpha.9",
|
|
47
|
+
"@elliemae/ds-typography": "3.70.0-alpha.9",
|
|
48
|
+
"@elliemae/ds-system": "3.70.0-alpha.9"
|
|
48
49
|
},
|
|
49
50
|
"devDependencies": {
|
|
51
|
+
"@playwright/experimental-ct-react": "1.61.1",
|
|
50
52
|
"jest": "^30.0.0",
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"@elliemae/ds-
|
|
53
|
+
"jest-axe": "^11.0.0",
|
|
54
|
+
"styled-components": "~5.3.11",
|
|
55
|
+
"@elliemae/ds-monorepo-devops": "3.70.0-alpha.9",
|
|
56
|
+
"@elliemae/ds-typescript-helpers": "3.70.0-alpha.9",
|
|
57
|
+
"@elliemae/ds-test-utils": "3.70.0-alpha.9"
|
|
54
58
|
},
|
|
55
59
|
"peerDependencies": {
|
|
56
|
-
"lodash-es": "^4.18.1",
|
|
57
60
|
"react": "^18.3.1",
|
|
58
61
|
"react-dom": "^18.3.1",
|
|
59
|
-
"styled-components": "~5.3.
|
|
62
|
+
"styled-components": "~5.3.11"
|
|
60
63
|
},
|
|
61
64
|
"publishConfig": {
|
|
62
65
|
"access": "public",
|
|
63
|
-
"typeSafety":
|
|
66
|
+
"typeSafety": true
|
|
64
67
|
},
|
|
65
68
|
"scripts": {
|
|
66
69
|
"dev": "cross-env NODE_ENV=development node ../../../scripts/build/build.mjs --watch",
|
|
67
|
-
"test": "ds-monorepo-devops test --passWithNoTests --coverage=\"false\"",
|
|
70
|
+
"test": "playwright test -c ./playwright.config.mjs && ds-monorepo-devops test --passWithNoTests --coverage=\"false\"",
|
|
68
71
|
"lint": "node ../../../scripts/lint.mjs --fix",
|
|
69
72
|
"lint:strict": "node ../../../scripts/lint-strict.mjs",
|
|
70
73
|
"dts": "node ../../../scripts/dts.mjs",
|