@multiplatform.one/forms 7.67.0 → 7.69.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/AdaptivePopup/index.cjs +45 -5
- package/dist/cjs/AdaptivePopup/index.native.js +47 -5
- package/dist/cjs/AdaptivePopup/index.native.js.map +1 -1
- package/dist/cjs/Button/IconButtonGroup.cjs +95 -0
- package/dist/cjs/Button/IconButtonGroup.native.js +101 -0
- package/dist/cjs/Button/IconButtonGroup.native.js.map +1 -0
- package/dist/cjs/Button/glass.cjs +34 -0
- package/dist/cjs/Button/glass.native.js +55 -0
- package/dist/cjs/Button/glass.native.js.map +1 -0
- package/dist/cjs/Button/iconButtonGroupContext.cjs +33 -0
- package/dist/cjs/Button/iconButtonGroupContext.native.js +36 -0
- package/dist/cjs/Button/iconButtonGroupContext.native.js.map +1 -0
- package/dist/cjs/Button/index.cjs +174 -30
- package/dist/cjs/Button/index.native.js +174 -29
- package/dist/cjs/Button/index.native.js.map +1 -1
- package/dist/cjs/Button/mediaPlate.cjs +31 -0
- package/dist/cjs/Button/mediaPlate.native.js +34 -0
- package/dist/cjs/Button/mediaPlate.native.js.map +1 -0
- package/dist/cjs/ContextualSaveBar/index.cjs +6 -8
- package/dist/cjs/ContextualSaveBar/index.native.js +6 -8
- package/dist/cjs/ContextualSaveBar/index.native.js.map +1 -1
- package/dist/cjs/DescriptionList/index.cjs +0 -1
- package/dist/cjs/DescriptionList/index.native.js +0 -1
- package/dist/cjs/DescriptionList/index.native.js.map +1 -1
- package/dist/cjs/FloatingPanel/index.native.js +21 -16
- package/dist/cjs/FloatingPanel/index.native.js.map +1 -1
- package/dist/cjs/fields/Barcode/BarcodeScanner.cjs +7 -8
- package/dist/cjs/fields/Barcode/BarcodeScanner.native.js +7 -8
- package/dist/cjs/fields/Barcode/BarcodeScanner.native.js.map +1 -1
- package/dist/cjs/fields/FileUpload/index.cjs +86 -60
- package/dist/cjs/fields/FileUpload/index.native.js +99 -73
- package/dist/cjs/fields/FileUpload/index.native.js.map +1 -1
- package/dist/cjs/fields/Geolocation/index.cjs +7 -8
- package/dist/cjs/fields/Geolocation/index.native.js +7 -8
- package/dist/cjs/fields/Geolocation/index.native.js.map +1 -1
- package/dist/cjs/fields/ImageField/index.cjs +4 -6
- package/dist/cjs/fields/ImageField/index.native.js +4 -6
- package/dist/cjs/fields/ImageField/index.native.js.map +1 -1
- package/dist/cjs/fields/nativeDateTime/OsDateTimeSheet.cjs +20 -28
- package/dist/cjs/fields/nativeDateTime/OsDateTimeSheet.native.js +20 -28
- package/dist/cjs/fields/nativeDateTime/OsDateTimeSheet.native.js.map +1 -1
- package/dist/cjs/index.cjs +11 -1
- package/dist/cjs/index.native.js +10 -0
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/cjs/shared/colorRamps.cjs +0 -8
- package/dist/cjs/shared/colorRamps.native.js +0 -8
- package/dist/cjs/shared/colorRamps.native.js.map +1 -1
- package/dist/cjs/standardActions.cjs +117 -0
- package/dist/cjs/standardActions.native.js +120 -0
- package/dist/cjs/standardActions.native.js.map +1 -0
- package/dist/esm/AdaptivePopup/index.mjs +45 -5
- package/dist/esm/AdaptivePopup/index.mjs.map +1 -1
- package/dist/esm/AdaptivePopup/index.native.js +47 -5
- package/dist/esm/AdaptivePopup/index.native.js.map +1 -1
- package/dist/esm/Button/IconButtonGroup.mjs +69 -0
- package/dist/esm/Button/IconButtonGroup.mjs.map +1 -0
- package/dist/esm/Button/IconButtonGroup.native.js +72 -0
- package/dist/esm/Button/IconButtonGroup.native.js.map +1 -0
- package/dist/esm/Button/glass.mjs +8 -0
- package/dist/esm/Button/glass.mjs.map +1 -0
- package/dist/esm/Button/glass.native.js +26 -0
- package/dist/esm/Button/glass.native.js.map +1 -0
- package/dist/esm/Button/iconButtonGroupContext.mjs +7 -0
- package/dist/esm/Button/iconButtonGroupContext.mjs.map +1 -0
- package/dist/esm/Button/iconButtonGroupContext.native.js +7 -0
- package/dist/esm/Button/iconButtonGroupContext.native.js.map +1 -0
- package/dist/esm/Button/index.mjs +166 -30
- package/dist/esm/Button/index.mjs.map +1 -1
- package/dist/esm/Button/index.native.js +167 -30
- package/dist/esm/Button/index.native.js.map +1 -1
- package/dist/esm/Button/mediaPlate.mjs +6 -0
- package/dist/esm/Button/mediaPlate.mjs.map +1 -0
- package/dist/esm/Button/mediaPlate.native.js +6 -0
- package/dist/esm/Button/mediaPlate.native.js.map +1 -0
- package/dist/esm/ContextualSaveBar/index.mjs +7 -9
- package/dist/esm/ContextualSaveBar/index.mjs.map +1 -1
- package/dist/esm/ContextualSaveBar/index.native.js +7 -9
- package/dist/esm/ContextualSaveBar/index.native.js.map +1 -1
- package/dist/esm/DescriptionList/index.mjs +0 -1
- package/dist/esm/DescriptionList/index.mjs.map +1 -1
- package/dist/esm/DescriptionList/index.native.js +0 -1
- package/dist/esm/DescriptionList/index.native.js.map +1 -1
- package/dist/esm/FloatingPanel/index.native.js +22 -17
- package/dist/esm/FloatingPanel/index.native.js.map +1 -1
- package/dist/esm/fields/Barcode/BarcodeScanner.mjs +8 -9
- package/dist/esm/fields/Barcode/BarcodeScanner.mjs.map +1 -1
- package/dist/esm/fields/Barcode/BarcodeScanner.native.js +8 -9
- package/dist/esm/fields/Barcode/BarcodeScanner.native.js.map +1 -1
- package/dist/esm/fields/FileUpload/index.mjs +87 -61
- package/dist/esm/fields/FileUpload/index.mjs.map +1 -1
- package/dist/esm/fields/FileUpload/index.native.js +100 -74
- package/dist/esm/fields/FileUpload/index.native.js.map +1 -1
- package/dist/esm/fields/Geolocation/index.mjs +8 -9
- package/dist/esm/fields/Geolocation/index.mjs.map +1 -1
- package/dist/esm/fields/Geolocation/index.native.js +8 -9
- package/dist/esm/fields/Geolocation/index.native.js.map +1 -1
- package/dist/esm/fields/ImageField/index.mjs +6 -8
- package/dist/esm/fields/ImageField/index.mjs.map +1 -1
- package/dist/esm/fields/ImageField/index.native.js +6 -8
- package/dist/esm/fields/ImageField/index.native.js.map +1 -1
- package/dist/esm/fields/nativeDateTime/OsDateTimeSheet.mjs +20 -28
- package/dist/esm/fields/nativeDateTime/OsDateTimeSheet.mjs.map +1 -1
- package/dist/esm/fields/nativeDateTime/OsDateTimeSheet.native.js +20 -28
- package/dist/esm/fields/nativeDateTime/OsDateTimeSheet.native.js.map +1 -1
- package/dist/esm/index.mjs +3 -2
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/index.native.js +3 -2
- package/dist/esm/index.native.js.map +1 -1
- package/dist/esm/shared/colorRamps.mjs +0 -8
- package/dist/esm/shared/colorRamps.mjs.map +1 -1
- package/dist/esm/shared/colorRamps.native.js +0 -8
- package/dist/esm/shared/colorRamps.native.js.map +1 -1
- package/dist/esm/standardActions.mjs +91 -0
- package/dist/esm/standardActions.mjs.map +1 -0
- package/dist/esm/standardActions.native.js +91 -0
- package/dist/esm/standardActions.native.js.map +1 -0
- package/dist/jsx/AdaptivePopup/index.mjs +45 -5
- package/dist/jsx/AdaptivePopup/index.mjs.map +1 -1
- package/dist/jsx/AdaptivePopup/index.native.js +47 -5
- package/dist/jsx/AdaptivePopup/index.native.js.map +1 -1
- package/dist/jsx/Button/IconButtonGroup.mjs +69 -0
- package/dist/jsx/Button/IconButtonGroup.mjs.map +1 -0
- package/dist/jsx/Button/IconButtonGroup.native.js +101 -0
- package/dist/jsx/Button/IconButtonGroup.native.js.map +1 -0
- package/dist/jsx/Button/glass.mjs +8 -0
- package/dist/jsx/Button/glass.mjs.map +1 -0
- package/dist/jsx/Button/glass.native.js +55 -0
- package/dist/jsx/Button/glass.native.js.map +1 -0
- package/dist/jsx/Button/iconButtonGroupContext.mjs +7 -0
- package/dist/jsx/Button/iconButtonGroupContext.mjs.map +1 -0
- package/dist/jsx/Button/iconButtonGroupContext.native.js +36 -0
- package/dist/jsx/Button/iconButtonGroupContext.native.js.map +1 -0
- package/dist/jsx/Button/index.mjs +166 -30
- package/dist/jsx/Button/index.mjs.map +1 -1
- package/dist/jsx/Button/index.native.js +174 -29
- package/dist/jsx/Button/index.native.js.map +1 -1
- package/dist/jsx/Button/mediaPlate.mjs +6 -0
- package/dist/jsx/Button/mediaPlate.mjs.map +1 -0
- package/dist/jsx/Button/mediaPlate.native.js +34 -0
- package/dist/jsx/Button/mediaPlate.native.js.map +1 -0
- package/dist/jsx/ContextualSaveBar/index.mjs +7 -9
- package/dist/jsx/ContextualSaveBar/index.mjs.map +1 -1
- package/dist/jsx/ContextualSaveBar/index.native.js +6 -8
- package/dist/jsx/ContextualSaveBar/index.native.js.map +1 -1
- package/dist/jsx/DescriptionList/index.mjs +0 -1
- package/dist/jsx/DescriptionList/index.mjs.map +1 -1
- package/dist/jsx/DescriptionList/index.native.js +0 -1
- package/dist/jsx/DescriptionList/index.native.js.map +1 -1
- package/dist/jsx/FloatingPanel/index.native.js +21 -16
- package/dist/jsx/FloatingPanel/index.native.js.map +1 -1
- package/dist/jsx/fields/Barcode/BarcodeScanner.mjs +8 -9
- package/dist/jsx/fields/Barcode/BarcodeScanner.mjs.map +1 -1
- package/dist/jsx/fields/Barcode/BarcodeScanner.native.js +7 -8
- package/dist/jsx/fields/Barcode/BarcodeScanner.native.js.map +1 -1
- package/dist/jsx/fields/FileUpload/index.mjs +87 -61
- package/dist/jsx/fields/FileUpload/index.mjs.map +1 -1
- package/dist/jsx/fields/FileUpload/index.native.js +99 -73
- package/dist/jsx/fields/FileUpload/index.native.js.map +1 -1
- package/dist/jsx/fields/Geolocation/index.mjs +8 -9
- package/dist/jsx/fields/Geolocation/index.mjs.map +1 -1
- package/dist/jsx/fields/Geolocation/index.native.js +7 -8
- package/dist/jsx/fields/Geolocation/index.native.js.map +1 -1
- package/dist/jsx/fields/ImageField/index.mjs +6 -8
- package/dist/jsx/fields/ImageField/index.mjs.map +1 -1
- package/dist/jsx/fields/ImageField/index.native.js +4 -6
- package/dist/jsx/fields/ImageField/index.native.js.map +1 -1
- package/dist/jsx/fields/nativeDateTime/OsDateTimeSheet.mjs +20 -28
- package/dist/jsx/fields/nativeDateTime/OsDateTimeSheet.mjs.map +1 -1
- package/dist/jsx/fields/nativeDateTime/OsDateTimeSheet.native.js +20 -28
- package/dist/jsx/fields/nativeDateTime/OsDateTimeSheet.native.js.map +1 -1
- package/dist/jsx/index.js +3 -2
- package/dist/jsx/index.js.map +1 -1
- package/dist/jsx/index.mjs +3 -2
- package/dist/jsx/index.mjs.map +1 -1
- package/dist/jsx/index.native.js +10 -0
- package/dist/jsx/index.native.js.map +1 -1
- package/dist/jsx/shared/colorRamps.mjs +0 -8
- package/dist/jsx/shared/colorRamps.mjs.map +1 -1
- package/dist/jsx/shared/colorRamps.native.js +0 -8
- package/dist/jsx/shared/colorRamps.native.js.map +1 -1
- package/dist/jsx/standardActions.mjs +91 -0
- package/dist/jsx/standardActions.mjs.map +1 -0
- package/dist/jsx/standardActions.native.js +120 -0
- package/dist/jsx/standardActions.native.js.map +1 -0
- package/package.json +12 -8
- package/src/AdaptivePopup/AdaptivePopup.spec.tsx +44 -0
- package/src/AdaptivePopup/AdaptivePopup.stories.tsx +12 -32
- package/src/AdaptivePopup/index.tsx +65 -13
- package/src/Button/Button.disabledGlyph.spec.tsx +111 -0
- package/src/Button/Button.iconButton.spec.tsx +341 -0
- package/src/Button/Button.stories.tsx +179 -2
- package/src/Button/IconButtonGroup.spec.tsx +126 -0
- package/src/Button/IconButtonGroup.tsx +87 -0
- package/src/Button/glass.native.ts +51 -0
- package/src/Button/glass.ts +33 -0
- package/src/Button/iconButtonGroupContext.ts +14 -0
- package/src/Button/index.tsx +314 -42
- package/src/Button/mediaPlate.ts +9 -0
- package/src/ContextualSaveBar/ContextualSaveBar.spec.tsx +3 -1
- package/src/ContextualSaveBar/ContextualSaveBar.stories.tsx +2 -2
- package/src/ContextualSaveBar/index.tsx +7 -10
- package/src/ControlGroup/ControlGroup.stories.tsx +3 -2
- package/src/DescriptionList/index.tsx +0 -1
- package/src/ErrorSummary.stories.tsx +2 -1
- package/src/FieldLayout.stories.tsx +3 -2
- package/src/FloatingPanel/FloatingPanel.pointMenu.native.spec.tsx +69 -0
- package/src/FloatingPanel/FloatingPanel.stories.tsx +6 -10
- package/src/FloatingPanel/index.native.tsx +25 -4
- package/src/Form/Form.stories.tsx +6 -5
- package/src/errorSummaryStories.spec.tsx +4 -4
- package/src/fields/Barcode/BarcodeScanner.native.tsx +10 -9
- package/src/fields/Barcode/BarcodeScanner.tsx +10 -9
- package/src/fields/Checkbox/Checkbox.stories.tsx +3 -3
- package/src/fields/Checkbox/CheckboxGroup.stories.tsx +2 -2
- package/src/fields/ColorPicker/ColorPicker.stories.tsx +2 -2
- package/src/fields/Combobox/Combobox.stories.tsx +2 -2
- package/src/fields/DatePicker/DatePicker.stories.tsx +2 -1
- package/src/fields/DatePicker/DateRangePicker.stories.tsx +2 -2
- package/src/fields/DatePicker/DatetimePicker.stories.tsx +2 -1
- package/src/fields/DatePicker/MonthPicker.stories.tsx +2 -2
- package/src/fields/DatePicker/MultiDatePicker.stories.tsx +2 -2
- package/src/fields/FileUpload/FileUpload.spec.tsx +14 -5
- package/src/fields/FileUpload/FileUpload.stories.tsx +2 -2
- package/src/fields/FileUpload/index.tsx +103 -77
- package/src/fields/Geolocation/index.tsx +12 -7
- package/src/fields/ImageField/ImageField.spec.tsx +5 -5
- package/src/fields/ImageField/ImageField.stories.tsx +2 -2
- package/src/fields/ImageField/index.tsx +6 -9
- package/src/fields/Input/Input.stories.tsx +2 -2
- package/src/fields/MentionInput/MentionInput.stories.tsx +2 -1
- package/src/fields/Progress/Progress.stories.tsx +2 -1
- package/src/fields/RadioGroup/RadioGroup.stories.tsx +2 -1
- package/src/fields/Signature/Signature.stories.tsx +2 -2
- package/src/fields/Stepper/Stepper.stories.tsx +2 -2
- package/src/fields/TextArea/TextArea.stories.tsx +2 -2
- package/src/fields/ToggleGroup/ToggleGroup.stories.tsx +2 -2
- package/src/fields/nativeDateTime/OsDateTimeSheet.presentation.spec.tsx +6 -5
- package/src/fields/nativeDateTime/OsDateTimeSheet.tsx +24 -12
- package/src/index.ts +18 -2
- package/src/shared/colorRamps.ts +1 -11
- package/src/standardActions.ts +96 -0
- package/types/AdaptivePopup/index.d.ts +13 -1
- package/types/AdaptivePopup/index.d.ts.map +1 -1
- package/types/Button/IconButtonGroup.d.ts +22 -0
- package/types/Button/IconButtonGroup.d.ts.map +1 -0
- package/types/Button/glass.d.ts +26 -0
- package/types/Button/glass.d.ts.map +1 -0
- package/types/Button/glass.native.d.ts +17 -0
- package/types/Button/glass.native.d.ts.map +1 -0
- package/types/Button/iconButtonGroupContext.d.ts +9 -0
- package/types/Button/iconButtonGroupContext.d.ts.map +1 -0
- package/types/Button/index.d.ts +55 -2
- package/types/Button/index.d.ts.map +1 -1
- package/types/Button/mediaPlate.d.ts +10 -0
- package/types/Button/mediaPlate.d.ts.map +1 -0
- package/types/ContextualSaveBar/index.d.ts.map +1 -1
- package/types/DescriptionList/index.d.ts.map +1 -1
- package/types/FloatingPanel/index.native.d.ts +2 -1
- package/types/FloatingPanel/index.native.d.ts.map +1 -1
- package/types/fields/Barcode/BarcodeScanner.d.ts.map +1 -1
- package/types/fields/Barcode/BarcodeScanner.native.d.ts.map +1 -1
- package/types/fields/FileUpload/index.d.ts.map +1 -1
- package/types/fields/Geolocation/index.d.ts.map +1 -1
- package/types/fields/ImageField/index.d.ts.map +1 -1
- package/types/fields/nativeDateTime/OsDateTimeSheet.d.ts.map +1 -1
- package/types/index.d.ts +4 -2
- package/types/index.d.ts.map +1 -1
- package/types/shared/colorRamps.d.ts +1 -11
- package/types/shared/colorRamps.d.ts.map +1 -1
- package/types/standardActions.d.ts +24 -0
- package/types/standardActions.d.ts.map +1 -0
|
@@ -10,6 +10,7 @@ import { Field, FieldLayout } from "./fieldLayout";
|
|
|
10
10
|
import { Input } from "./fields/Input";
|
|
11
11
|
import { Input as InputParts } from "./InputParts";
|
|
12
12
|
import { Checkbox } from "./fields/Checkbox";
|
|
13
|
+
import { ActionButton } from "@multiplatform.one/components";
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* FieldLayout wraps a CONTROL, so these stories cannot mount the house `Input`
|
|
@@ -196,7 +197,7 @@ export const FieldWrapper: Story = {
|
|
|
196
197
|
)}
|
|
197
198
|
</Field>
|
|
198
199
|
<XStack gap="$2">
|
|
199
|
-
<
|
|
200
|
+
<ActionButton kind="submit" formAction="submit" />
|
|
200
201
|
<Button onPress={() => form.reset()}>Reset</Button>
|
|
201
202
|
</XStack>
|
|
202
203
|
</YStack>
|
|
@@ -237,7 +238,7 @@ export const FieldRequiredValidator: Story = {
|
|
|
237
238
|
</FieldLayout>
|
|
238
239
|
)}
|
|
239
240
|
</Field>
|
|
240
|
-
<
|
|
241
|
+
<ActionButton kind="submit" formAction="submit" />
|
|
241
242
|
</YStack>
|
|
242
243
|
</Form>
|
|
243
244
|
);
|
|
@@ -124,3 +124,72 @@ describe("FloatingPanel native ContextMenu at the long-press point", () => {
|
|
|
124
124
|
expect(sheet()).toBeNull();
|
|
125
125
|
});
|
|
126
126
|
});
|
|
127
|
+
|
|
128
|
+
function Glyph(_: { onPress: () => void; "aria-label": string }) {
|
|
129
|
+
return <Text>◐</Text>;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function GlyphPanel({ onPress, anchored = false }: { onPress: () => void; anchored?: boolean }) {
|
|
133
|
+
const [open, setOpen] = useState(false);
|
|
134
|
+
return (
|
|
135
|
+
<FloatingPanel
|
|
136
|
+
open={open}
|
|
137
|
+
onOpenChange={setOpen}
|
|
138
|
+
openOn="contextmenu"
|
|
139
|
+
triggerSizing="content"
|
|
140
|
+
{...(anchored
|
|
141
|
+
? { compactFace: "menu" as const, anchorMode: "attach" as const, triggerEcho: false }
|
|
142
|
+
: null)}
|
|
143
|
+
trigger={<Glyph onPress={onPress} aria-label="Appearance" />}
|
|
144
|
+
>
|
|
145
|
+
<Text>Dark</Text>
|
|
146
|
+
</FloatingPanel>
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function tap(target: Element) {
|
|
151
|
+
act(() => {
|
|
152
|
+
target.dispatchEvent(new MouseEvent("mousedown", { bubbles: true, button: 0 }));
|
|
153
|
+
vi.advanceTimersByTime(100);
|
|
154
|
+
target.dispatchEvent(new MouseEvent("mouseup", { bubbles: true, button: 0 }));
|
|
155
|
+
target.dispatchEvent(new MouseEvent("click", { bubbles: true, button: 0 }));
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
describe("FloatingPanel native ContextMenu keeps its trigger's tap (FZ-8158)", () => {
|
|
160
|
+
it("runs the trigger's own onPress on a tap and names the button after it", () => {
|
|
161
|
+
screen.width = 390;
|
|
162
|
+
screen.height = 844;
|
|
163
|
+
const onPress = vi.fn();
|
|
164
|
+
const result = renderWithProviders(<GlyphPanel onPress={onPress} />);
|
|
165
|
+
tap(result.getByRole("button", { name: "Appearance" }));
|
|
166
|
+
expect(onPress).toHaveBeenCalledTimes(1);
|
|
167
|
+
expect(sheet()).toBeNull();
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it("opens the menu on a long-press without running the tap", () => {
|
|
171
|
+
screen.width = 390;
|
|
172
|
+
screen.height = 844;
|
|
173
|
+
const onPress = vi.fn();
|
|
174
|
+
const result = renderWithProviders(<GlyphPanel onPress={onPress} />);
|
|
175
|
+
longPress(result.getByRole("button", { name: "Appearance" }), 200, 300);
|
|
176
|
+
expect(sheet()).not.toBeNull();
|
|
177
|
+
expect(onPress).not.toHaveBeenCalled();
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it("opens as the pull-down under its trigger on a phone when menu-faced, never a sheet (FZ-8158 r5)", () => {
|
|
181
|
+
screen.width = 390;
|
|
182
|
+
screen.height = 844;
|
|
183
|
+
const onPress = vi.fn();
|
|
184
|
+
const result = renderWithProviders(<GlyphPanel onPress={onPress} anchored />);
|
|
185
|
+
longPress(result.getByRole("button", { name: "Appearance" }), 200, 300);
|
|
186
|
+
act(() => {
|
|
187
|
+
vi.advanceTimersByTime(1);
|
|
188
|
+
});
|
|
189
|
+
expect(document.querySelector('[data-testid="floating-panel-menu"]')).not.toBeNull();
|
|
190
|
+
expect(document.querySelector('[data-testid="floating-panel-menu-echo"]')).toBeNull();
|
|
191
|
+
expect(sheet()).toBeNull();
|
|
192
|
+
expect(pointPanel()).toBeNull();
|
|
193
|
+
expect(onPress).not.toHaveBeenCalled();
|
|
194
|
+
});
|
|
195
|
+
});
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { useState } from "react";
|
|
2
2
|
import type { Meta, StoryObj } from "@storybook/react-native-web-vite";
|
|
3
|
-
import { Preset, Paragraph,
|
|
4
|
-
import { ScrollView,
|
|
3
|
+
import { Preset, Paragraph, Text } from "@multiplatform.one/theme";
|
|
4
|
+
import { ScrollView, View, XStack, YStack } from "tamagui";
|
|
5
|
+
import { OverlayHeader } from "@multiplatform.one/components";
|
|
5
6
|
import { FloatingPanel } from "./index";
|
|
6
7
|
import { Button } from "../Button";
|
|
7
8
|
import { Select } from "../fields/Select";
|
|
@@ -38,13 +39,11 @@ export const Basic: Story = {
|
|
|
38
39
|
trigger={<Button>{open ? "Panel is open" : "Click to open panel"}</Button>}
|
|
39
40
|
>
|
|
40
41
|
<YStack gap="$3">
|
|
41
|
-
<
|
|
42
|
+
<OverlayHeader title="Panel Content" onClose={() => setOpen(false)} />
|
|
42
43
|
<Paragraph>
|
|
43
44
|
Opens over its trigger: the top edge sits on the trigger's top edge and the trigger is
|
|
44
45
|
the first row. Escape or an outside click dismisses and returns focus.
|
|
45
46
|
</Paragraph>
|
|
46
|
-
<Separator />
|
|
47
|
-
<Button onPress={() => setOpen(false)}>Close</Button>
|
|
48
47
|
</YStack>
|
|
49
48
|
</FloatingPanel>
|
|
50
49
|
</YStack>
|
|
@@ -185,9 +184,8 @@ export const ForceSheet: Story = {
|
|
|
185
184
|
trigger={<Button>{open ? "Sheet open" : "Click for Sheet (forced)"}</Button>}
|
|
186
185
|
>
|
|
187
186
|
<YStack gap="$3">
|
|
188
|
-
<
|
|
187
|
+
<OverlayHeader title="Forced Sheet" onClose={() => setOpen(false)} />
|
|
189
188
|
<Paragraph>This panel always uses Sheet, even on large screens.</Paragraph>
|
|
190
|
-
<Button onPress={() => setOpen(false)}>Close</Button>
|
|
191
189
|
</YStack>
|
|
192
190
|
</FloatingPanel>
|
|
193
191
|
</YStack>
|
|
@@ -224,13 +222,11 @@ function ScrollClipPair({ where }: { where: string }) {
|
|
|
224
222
|
trigger={<Button>{`FloatingPanel (${where})`}</Button>}
|
|
225
223
|
>
|
|
226
224
|
<YStack gap="$3" data-testid={`scroll-clip-panel-${where}`}>
|
|
227
|
-
<
|
|
225
|
+
<OverlayHeader title={`Panel ${where}`} onClose={() => setOpen(false)} />
|
|
228
226
|
<Paragraph>
|
|
229
227
|
This body is taller than the space left in the scroll view, so a clipped panel loses its
|
|
230
228
|
lower half.
|
|
231
229
|
</Paragraph>
|
|
232
|
-
<Separator />
|
|
233
|
-
<Button onPress={() => setOpen(false)}>Close</Button>
|
|
234
230
|
</YStack>
|
|
235
231
|
</FloatingPanel>
|
|
236
232
|
</YStack>
|
|
@@ -54,7 +54,8 @@
|
|
|
54
54
|
* ContextMenu (`openOn="contextmenu"`): long-press opens it. On a window wider
|
|
55
55
|
* than OVERLAY_BREAKPOINT (a tablet) it opens at the long-press point, the
|
|
56
56
|
* same free overlay the web draws at the pointer; a phone keeps the sheet,
|
|
57
|
-
* as web does at ≤640.
|
|
57
|
+
* as web does at ≤640. With `compactFace="menu"` it opens as the pull-down at
|
|
58
|
+
* its trigger instead, at every width (a bar button's long-press, FZ-8158).
|
|
58
59
|
*
|
|
59
60
|
* Regular width (FZ-8116, LC-266): on an iPad or any window wider than
|
|
60
61
|
* OVERLAY_BREAKPOINT the sheet is the wrong form. The panel opens over its
|
|
@@ -64,6 +65,7 @@
|
|
|
64
65
|
|
|
65
66
|
import { nativeAnimationConfig, type MotionStop } from "@multiplatform.one/theme";
|
|
66
67
|
import {
|
|
68
|
+
isValidElement,
|
|
67
69
|
type ReactNode,
|
|
68
70
|
useCallback,
|
|
69
71
|
useContext,
|
|
@@ -208,10 +210,25 @@ export function FloatingPanel({
|
|
|
208
210
|
[disabled, onOpenChange],
|
|
209
211
|
);
|
|
210
212
|
const isContextMenu = openOn === "contextmenu";
|
|
213
|
+
// A long-press menu never eats its trigger's tap: the web lets the primary
|
|
214
|
+
// click through to the trigger, so here the Pressable that owns the gesture
|
|
215
|
+
// runs the trigger's own onPress and carries its name.
|
|
216
|
+
const triggerOwn =
|
|
217
|
+
isContextMenu && isValidElement(trigger)
|
|
218
|
+
? (trigger.props as {
|
|
219
|
+
onPress?: (event: GestureResponderEvent) => void;
|
|
220
|
+
"aria-label"?: string;
|
|
221
|
+
accessibilityLabel?: string;
|
|
222
|
+
})
|
|
223
|
+
: undefined;
|
|
224
|
+
const triggerLabel =
|
|
225
|
+
triggerA11y?.label ?? triggerOwn?.["aria-label"] ?? triggerOwn?.accessibilityLabel;
|
|
211
226
|
const regular = overlaySizeClass(windowWidth) === "regular";
|
|
212
227
|
const atPoint = isContextMenu && pressPoint !== null && regular;
|
|
213
228
|
const insideOverlay = useInsideOverlay();
|
|
214
|
-
const asMenu =
|
|
229
|
+
const asMenu = isContextMenu
|
|
230
|
+
? compactFace === "menu"
|
|
231
|
+
: resolveCompactFace(compactFace, insideOverlay) === "menu";
|
|
215
232
|
const asRegular = regular && !asMenu && !isContextMenu && sheet !== true;
|
|
216
233
|
const anchored = asMenu || asRegular;
|
|
217
234
|
const triggerRef = useRef<RNView | null>(null);
|
|
@@ -543,12 +560,16 @@ export function FloatingPanel({
|
|
|
543
560
|
>
|
|
544
561
|
<Pressable
|
|
545
562
|
{...(isContextMenu
|
|
546
|
-
? {
|
|
563
|
+
? {
|
|
564
|
+
onLongPress: handleTriggerLongPress,
|
|
565
|
+
delayLongPress: 500,
|
|
566
|
+
...(triggerOwn?.onPress && !disabled ? { onPress: triggerOwn.onPress } : undefined),
|
|
567
|
+
}
|
|
547
568
|
: { onPress: handleTriggerPress })}
|
|
548
569
|
disabled={disabled}
|
|
549
570
|
accessibilityRole="button"
|
|
550
571
|
accessibilityState={{ disabled: !!disabled, expanded: !!open }}
|
|
551
|
-
{...(
|
|
572
|
+
{...(triggerLabel ? { accessibilityLabel: triggerLabel } : undefined)}
|
|
552
573
|
{...(triggerA11y?.value
|
|
553
574
|
? { accessibilityValue: { text: triggerA11y.value } }
|
|
554
575
|
: undefined)}
|
|
@@ -53,6 +53,7 @@ import { formatFieldError } from "../ErrorSummary";
|
|
|
53
53
|
import type { AnyFormApi } from "../types";
|
|
54
54
|
import { Form, FormActions } from "./index";
|
|
55
55
|
import { H3 } from "@multiplatform.one/theme";
|
|
56
|
+
import { ActionButton } from "@multiplatform.one/components";
|
|
56
57
|
|
|
57
58
|
const meta: Meta<typeof Form> = {
|
|
58
59
|
title: "Forms/Form",
|
|
@@ -95,7 +96,7 @@ export const Basic = () => (
|
|
|
95
96
|
<Input label="Username" name="username" required />
|
|
96
97
|
<Input label="Email" name="email" required />
|
|
97
98
|
<FormActions>
|
|
98
|
-
<
|
|
99
|
+
<ActionButton kind="submit" formAction="submit" />
|
|
99
100
|
</FormActions>
|
|
100
101
|
</Form>
|
|
101
102
|
);
|
|
@@ -292,7 +293,7 @@ export const DynamicFields = () => {
|
|
|
292
293
|
})()}
|
|
293
294
|
|
|
294
295
|
<FormActions>
|
|
295
|
-
<
|
|
296
|
+
<ActionButton kind="submit" formAction="submit" />
|
|
296
297
|
</FormActions>
|
|
297
298
|
</Form>
|
|
298
299
|
);
|
|
@@ -313,7 +314,7 @@ export const ReadOnlyAndDisabledSubmit = () => (
|
|
|
313
314
|
<Input label="Read Only" name="readOnlyField" readOnly />
|
|
314
315
|
<Input label="Disabled" name="disabledField" disabled />
|
|
315
316
|
<FormActions>
|
|
316
|
-
<
|
|
317
|
+
<ActionButton kind="submit" formAction="submit" />
|
|
317
318
|
</FormActions>
|
|
318
319
|
</Form>
|
|
319
320
|
);
|
|
@@ -342,7 +343,7 @@ export const InputWithLinkResolver = () => (
|
|
|
342
343
|
linkResolver={(v) => (v ? (`/items/${String(v)}` as any) : null)}
|
|
343
344
|
/>
|
|
344
345
|
<FormActions>
|
|
345
|
-
<
|
|
346
|
+
<ActionButton kind="submit" formAction="submit" />
|
|
346
347
|
</FormActions>
|
|
347
348
|
</Form>
|
|
348
349
|
);
|
|
@@ -999,7 +1000,7 @@ export const Layout = () => (
|
|
|
999
1000
|
</FormSection>
|
|
1000
1001
|
</FormSectionStack>
|
|
1001
1002
|
<FormActions>
|
|
1002
|
-
<
|
|
1003
|
+
<ActionButton kind="save" formAction="submit" />
|
|
1003
1004
|
</FormActions>
|
|
1004
1005
|
</Form>
|
|
1005
1006
|
);
|
|
@@ -86,17 +86,17 @@ describe("Forms/ErrorSummary stories", () => {
|
|
|
86
86
|
});
|
|
87
87
|
|
|
88
88
|
it("FocusesTheFieldOnPress starts clean and raises the summary on submit", () => {
|
|
89
|
-
const { container,
|
|
89
|
+
const { container, getByRole } = mount(FocusesTheFieldOnPress as StoryLike);
|
|
90
90
|
expect(container.querySelector("[data-testid='error-summary']")).toBeNull();
|
|
91
|
-
fireEvent.click(
|
|
91
|
+
fireEvent.click(getByRole("button", { name: "Submit" }));
|
|
92
92
|
const summary = container.querySelector("[data-testid='error-summary']");
|
|
93
93
|
expect(summary).toBeTruthy();
|
|
94
94
|
expect(container.querySelectorAll("a").length).toBe(2);
|
|
95
95
|
});
|
|
96
96
|
|
|
97
97
|
it("FocusesTheFieldOnPress links target the ids its fields actually carry", () => {
|
|
98
|
-
const { container,
|
|
99
|
-
fireEvent.click(
|
|
98
|
+
const { container, getByRole } = mount(FocusesTheFieldOnPress as StoryLike);
|
|
99
|
+
fireEvent.click(getByRole("button", { name: "Submit" }));
|
|
100
100
|
const hrefs = Array.from(container.querySelectorAll("a")).map((a) => a.getAttribute("href"));
|
|
101
101
|
expect(hrefs).toEqual(["#email", "#password"]);
|
|
102
102
|
expect(container.querySelector("#email")).toBeTruthy();
|
|
@@ -2,7 +2,8 @@ import { Camera, CameraView, useCameraPermissions } from "expo-camera";
|
|
|
2
2
|
import { useEffect, useRef, useState } from "react";
|
|
3
3
|
import { StyleSheet } from "react-native";
|
|
4
4
|
import { Text, View, XStack, YStack } from "tamagui";
|
|
5
|
-
import { Button } from "../../Button";
|
|
5
|
+
import { Button, IconButton } from "../../Button";
|
|
6
|
+
import { standardActions } from "../../standardActions";
|
|
6
7
|
import { formCommonColors, formInputColors } from "../../shared/colorRamps";
|
|
7
8
|
import { t } from "../../shared/t";
|
|
8
9
|
import type { BarcodeScannerProps, CameraPreflightResult } from "./scannerTypes";
|
|
@@ -95,8 +96,8 @@ export function BarcodeScanner({
|
|
|
95
96
|
{t("Camera permission denied. Please allow camera access.")}
|
|
96
97
|
</Text>
|
|
97
98
|
<XStack gap="$2">
|
|
98
|
-
<
|
|
99
|
-
|
|
99
|
+
<IconButton
|
|
100
|
+
icon={standardActions.retry.icon}
|
|
100
101
|
onPress={() => {
|
|
101
102
|
setDenied(false);
|
|
102
103
|
requestPermission().then((res) => {
|
|
@@ -104,12 +105,12 @@ export function BarcodeScanner({
|
|
|
104
105
|
});
|
|
105
106
|
}}
|
|
106
107
|
aria-label={t("Retry camera")}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
{t("Close")}
|
|
112
|
-
|
|
108
|
+
/>
|
|
109
|
+
<IconButton
|
|
110
|
+
icon={standardActions.close.icon}
|
|
111
|
+
onPress={onClose}
|
|
112
|
+
aria-label={t("Close scanner")}
|
|
113
|
+
/>
|
|
113
114
|
</XStack>
|
|
114
115
|
</YStack>
|
|
115
116
|
) : !permission?.granted ? (
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useEffect, useRef, useState } from "react";
|
|
2
2
|
import { Text, View, XStack, YStack, isWeb } from "tamagui";
|
|
3
|
-
import { Button } from "../../Button";
|
|
3
|
+
import { Button, IconButton } from "../../Button";
|
|
4
|
+
import { standardActions } from "../../standardActions";
|
|
4
5
|
import { queryPermissionState } from "../../shared/capabilityPreflight";
|
|
5
6
|
import { formCommonColors, formInputColors } from "../../shared/colorRamps";
|
|
6
7
|
import { t } from "../../shared/t";
|
|
@@ -173,16 +174,16 @@ export function BarcodeScanner({
|
|
|
173
174
|
{errorMessage}
|
|
174
175
|
</Text>
|
|
175
176
|
<XStack gap="$2">
|
|
176
|
-
<
|
|
177
|
-
|
|
177
|
+
<IconButton
|
|
178
|
+
icon={standardActions.retry.icon}
|
|
178
179
|
onPress={() => setAttempt((a) => a + 1)}
|
|
179
180
|
aria-label={t("Retry camera")}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
{t("Close")}
|
|
185
|
-
|
|
181
|
+
/>
|
|
182
|
+
<IconButton
|
|
183
|
+
icon={standardActions.close.icon}
|
|
184
|
+
onPress={onClose}
|
|
185
|
+
aria-label={t("Close scanner")}
|
|
186
|
+
/>
|
|
186
187
|
</XStack>
|
|
187
188
|
</YStack>
|
|
188
189
|
) : (
|
|
@@ -6,10 +6,10 @@ import { useForm } from "@tanstack/react-form";
|
|
|
6
6
|
import { useState } from "react";
|
|
7
7
|
import { Checkbox as TamaguiCheckbox, XStack, YStack } from "tamagui";
|
|
8
8
|
import { Label } from "../../Label";
|
|
9
|
-
import { Button } from "../../Button";
|
|
10
9
|
import { Form } from "../../Form";
|
|
11
10
|
import { Checkbox, Checkboxes, CheckboxGroup } from "./index";
|
|
12
11
|
import { Paragraph } from "@multiplatform.one/theme";
|
|
12
|
+
import { ActionButton } from "@multiplatform.one/components";
|
|
13
13
|
|
|
14
14
|
const meta: Meta<typeof Checkbox> = {
|
|
15
15
|
title: "Forms/Checkbox",
|
|
@@ -54,7 +54,7 @@ export const Main: Story = {
|
|
|
54
54
|
validators={{ onChange: ({ value }) => (!value ? "Terms are required" : undefined) }}
|
|
55
55
|
/>
|
|
56
56
|
<Checkbox label="Subscribe to newsletter" name="newsletter" />
|
|
57
|
-
<
|
|
57
|
+
<ActionButton kind="submit" formAction="submit" />
|
|
58
58
|
</Form>
|
|
59
59
|
);
|
|
60
60
|
};
|
|
@@ -274,7 +274,7 @@ export const CheckboxGroupWithForm: StoryObj = {
|
|
|
274
274
|
helperText="Select required permissions"
|
|
275
275
|
required
|
|
276
276
|
/>
|
|
277
|
-
<
|
|
277
|
+
<ActionButton kind="submit" formAction="submit" />
|
|
278
278
|
</YStack>
|
|
279
279
|
</Form>
|
|
280
280
|
);
|
|
@@ -3,9 +3,9 @@ import type { Meta, StoryObj } from "@storybook/react-native-web-vite";
|
|
|
3
3
|
import { Preset, Paragraph } from "@multiplatform.one/theme";
|
|
4
4
|
import { useForm } from "@tanstack/react-form";
|
|
5
5
|
import { YStack } from "tamagui";
|
|
6
|
-
import { Button } from "../../Button";
|
|
7
6
|
import { Form } from "../../Form";
|
|
8
7
|
import { CheckboxGroup } from "./CheckboxGroup";
|
|
8
|
+
import { ActionButton } from "@multiplatform.one/components";
|
|
9
9
|
|
|
10
10
|
const meta: Meta<typeof CheckboxGroup> = {
|
|
11
11
|
title: "Forms/CheckboxGroup",
|
|
@@ -255,7 +255,7 @@ export const RequiredForm: Story = {
|
|
|
255
255
|
]}
|
|
256
256
|
helperText="Submit empty to trigger required error"
|
|
257
257
|
/>
|
|
258
|
-
<
|
|
258
|
+
<ActionButton kind="submit" formAction="submit" />
|
|
259
259
|
</YStack>
|
|
260
260
|
</Form>
|
|
261
261
|
);
|
|
@@ -4,9 +4,9 @@ import type { Meta, StoryObj } from "@storybook/react-native-web-vite";
|
|
|
4
4
|
import { useForm } from "@tanstack/react-form";
|
|
5
5
|
import { useState } from "react";
|
|
6
6
|
import { YStack } from "tamagui";
|
|
7
|
-
import { Button } from "../../Button";
|
|
8
7
|
import { Form } from "../../Form";
|
|
9
8
|
import { ColorPicker } from "./index";
|
|
9
|
+
import { ActionButton } from "@multiplatform.one/components";
|
|
10
10
|
|
|
11
11
|
const meta: Meta<typeof ColorPicker> = {
|
|
12
12
|
title: "Forms/ColorPicker",
|
|
@@ -69,7 +69,7 @@ export const Main: Story = {
|
|
|
69
69
|
helperText="Choose background color"
|
|
70
70
|
/>
|
|
71
71
|
<ColorPicker label="Text Color" name="textColor" helperText="Choose text color" />
|
|
72
|
-
<
|
|
72
|
+
<ActionButton kind="submit" formAction="submit" />
|
|
73
73
|
</YStack>
|
|
74
74
|
</Form>
|
|
75
75
|
);
|
|
@@ -4,11 +4,11 @@ import type { Meta, StoryObj } from "@storybook/react-native-web-vite";
|
|
|
4
4
|
import { useForm } from "@tanstack/react-form";
|
|
5
5
|
import { useState } from "react";
|
|
6
6
|
import { XStack, YStack } from "tamagui";
|
|
7
|
-
import { Button } from "../../Button";
|
|
8
7
|
import { Form } from "../../Form";
|
|
9
8
|
import { Combobox, type ComboboxOption } from "./index";
|
|
10
9
|
import { authorOptions } from "./authorOptions";
|
|
11
10
|
import { Text } from "@multiplatform.one/theme";
|
|
11
|
+
import { ActionButton } from "@multiplatform.one/components";
|
|
12
12
|
|
|
13
13
|
const meta: Meta<typeof Combobox> = {
|
|
14
14
|
title: "Forms/Combobox",
|
|
@@ -117,7 +117,7 @@ export const Main: StoryObj<typeof Combobox> = {
|
|
|
117
117
|
searchPlaceholder="Type to search..."
|
|
118
118
|
/>
|
|
119
119
|
|
|
120
|
-
<
|
|
120
|
+
<ActionButton kind="submit" formAction="submit" />
|
|
121
121
|
</YStack>
|
|
122
122
|
</Form>
|
|
123
123
|
);
|
|
@@ -14,6 +14,7 @@ import { MonthPicker } from "./MonthPicker";
|
|
|
14
14
|
import { MultiDatePicker } from "./MultiDatePicker";
|
|
15
15
|
import { DatePicker } from "./index";
|
|
16
16
|
import { formatCalendarDate, Text } from "@multiplatform.one/theme";
|
|
17
|
+
import { ActionButton } from "@multiplatform.one/components";
|
|
17
18
|
import { TimePicker } from "../TimePicker";
|
|
18
19
|
|
|
19
20
|
const meta: Meta<typeof DatePicker> = {
|
|
@@ -86,7 +87,7 @@ export const Main: Story = {
|
|
|
86
87
|
name={"endDate" as any}
|
|
87
88
|
helperText="Select project end date"
|
|
88
89
|
/>
|
|
89
|
-
<
|
|
90
|
+
<ActionButton kind="submit" formAction="submit" />
|
|
90
91
|
</YStack>
|
|
91
92
|
</Form>
|
|
92
93
|
);
|
|
@@ -4,10 +4,10 @@ import type { Meta, StoryObj } from "@storybook/react-native-web-vite";
|
|
|
4
4
|
import { useForm } from "@tanstack/react-form";
|
|
5
5
|
import { useState } from "react";
|
|
6
6
|
import { YStack } from "tamagui";
|
|
7
|
-
import { Button } from "../../Button";
|
|
8
7
|
import { Form } from "../../Form";
|
|
9
8
|
import { DateRangePicker } from "./DateRangePicker";
|
|
10
9
|
import { Text } from "@multiplatform.one/theme";
|
|
10
|
+
import { ActionButton } from "@multiplatform.one/components";
|
|
11
11
|
|
|
12
12
|
const meta: Meta<typeof DateRangePicker> = {
|
|
13
13
|
title: "Forms/DateRangePicker",
|
|
@@ -103,7 +103,7 @@ export const FormBound: Story = {
|
|
|
103
103
|
<FormSubscribe selector={(s: any) => [s.values.range]}>
|
|
104
104
|
{([range]: any[]) => <Text>{`form-value:${JSON.stringify(range)}`}</Text>}
|
|
105
105
|
</FormSubscribe>
|
|
106
|
-
<
|
|
106
|
+
<ActionButton kind="submit" formAction="submit" />
|
|
107
107
|
</YStack>
|
|
108
108
|
</Form>
|
|
109
109
|
);
|
|
@@ -8,6 +8,7 @@ import { Button } from "../../Button";
|
|
|
8
8
|
import { Form } from "../../Form";
|
|
9
9
|
import { DatetimePicker } from "./DatetimePicker";
|
|
10
10
|
import { Text } from "@multiplatform.one/theme";
|
|
11
|
+
import { ActionButton } from "@multiplatform.one/components";
|
|
11
12
|
|
|
12
13
|
const meta: Meta<typeof DatetimePicker> = {
|
|
13
14
|
title: "Forms/DatetimePicker",
|
|
@@ -170,7 +171,7 @@ export const FormBound: Story = {
|
|
|
170
171
|
</Text>
|
|
171
172
|
)}
|
|
172
173
|
</FormSubscribe>
|
|
173
|
-
<
|
|
174
|
+
<ActionButton kind="submit" formAction="submit" />
|
|
174
175
|
</YStack>
|
|
175
176
|
</Form>
|
|
176
177
|
);
|
|
@@ -2,10 +2,10 @@ import { action } from "@multiplatform.one/storybook";
|
|
|
2
2
|
import type { Meta, StoryObj } from "@storybook/react-native-web-vite";
|
|
3
3
|
import { useForm } from "@tanstack/react-form";
|
|
4
4
|
import { YStack } from "tamagui";
|
|
5
|
-
import { Button } from "../../Button";
|
|
6
5
|
import { Form } from "../../Form";
|
|
7
6
|
import { MonthPicker } from "./MonthPicker";
|
|
8
7
|
import { Text } from "@multiplatform.one/theme";
|
|
8
|
+
import { ActionButton } from "@multiplatform.one/components";
|
|
9
9
|
|
|
10
10
|
const meta: Meta<typeof MonthPicker> = {
|
|
11
11
|
title: "Forms/MonthPicker",
|
|
@@ -104,7 +104,7 @@ export const FormBound: Story = {
|
|
|
104
104
|
<FormSubscribe selector={(s: any) => [s.values.period]}>
|
|
105
105
|
{([period]: any[]) => <Text>{`form-value:${JSON.stringify(period)}`}</Text>}
|
|
106
106
|
</FormSubscribe>
|
|
107
|
-
<
|
|
107
|
+
<ActionButton kind="submit" formAction="submit" />
|
|
108
108
|
</YStack>
|
|
109
109
|
</Form>
|
|
110
110
|
);
|
|
@@ -4,10 +4,10 @@ import type { Meta, StoryObj } from "@storybook/react-native-web-vite";
|
|
|
4
4
|
import { useForm } from "@tanstack/react-form";
|
|
5
5
|
import { useState } from "react";
|
|
6
6
|
import { YStack } from "tamagui";
|
|
7
|
-
import { Button } from "../../Button";
|
|
8
7
|
import { Form } from "../../Form";
|
|
9
8
|
import { MultiDatePicker } from "./MultiDatePicker";
|
|
10
9
|
import { Text } from "@multiplatform.one/theme";
|
|
10
|
+
import { ActionButton } from "@multiplatform.one/components";
|
|
11
11
|
|
|
12
12
|
const meta: Meta<typeof MultiDatePicker> = {
|
|
13
13
|
title: "Forms/MultiDatePicker",
|
|
@@ -115,7 +115,7 @@ export const FormBound: Story = {
|
|
|
115
115
|
</Text>
|
|
116
116
|
)}
|
|
117
117
|
</FormSubscribe>
|
|
118
|
-
<
|
|
118
|
+
<ActionButton kind="submit" formAction="submit" />
|
|
119
119
|
</YStack>
|
|
120
120
|
</Form>
|
|
121
121
|
);
|
|
@@ -67,7 +67,16 @@ describe("FileUpload", () => {
|
|
|
67
67
|
const result = renderWithProviders(
|
|
68
68
|
<FileUpload existing={{ src: "/files/sprite.png" }} onExistingRemove={remove} />,
|
|
69
69
|
);
|
|
70
|
-
|
|
70
|
+
// LC-260: Replace and Remove are glyphs over the image; LC-176 puts the
|
|
71
|
+
// pair in one shared media capsule, so the buttons draw glyphs alone.
|
|
72
|
+
const actions = result.container.querySelector('[data-file-upload="preview-actions"]');
|
|
73
|
+
const capsule = actions?.querySelector('[data-mp-icon-button-group="media"]');
|
|
74
|
+
expect(capsule?.querySelector('[aria-label="Replace"]')).toBeTruthy();
|
|
75
|
+
expect(capsule?.querySelector('[aria-label^="Remove"]')).toBeTruthy();
|
|
76
|
+
expect(
|
|
77
|
+
capsule?.querySelector('[aria-label="Replace"]')?.getAttribute("data-mp-icon-button"),
|
|
78
|
+
).not.toBe("media");
|
|
79
|
+
expect(result.container.textContent).not.toContain("Replace");
|
|
71
80
|
expect(result.container.textContent).not.toContain("Choose File");
|
|
72
81
|
expect(result.container.textContent).not.toContain("Drag a file");
|
|
73
82
|
expect(result.container.querySelector('[data-file-upload="preview"] img')).toBeTruthy();
|
|
@@ -344,7 +353,7 @@ describe("FileUpload bento-derived dropzone (MPO-57)", () => {
|
|
|
344
353
|
expect(zone.textContent).toContain("Drag a file into this area");
|
|
345
354
|
});
|
|
346
355
|
|
|
347
|
-
it("renders picked files as in-zone rows: name, bold size line,
|
|
356
|
+
it("renders picked files as in-zone rows: name, bold size line, bare remove glyph (29 painted, hitSlop 44)", async () => {
|
|
348
357
|
const { container } = renderWithHouseThemes(
|
|
349
358
|
<FileUpload label="Attachment" name="attachment" multiple />,
|
|
350
359
|
);
|
|
@@ -367,14 +376,14 @@ describe("FileUpload bento-derived dropzone (MPO-57)", () => {
|
|
|
367
376
|
expect(sizeLine).toBeTruthy();
|
|
368
377
|
expect(sizeLine.className).toMatch(/_fow-700/);
|
|
369
378
|
|
|
370
|
-
// Remove:
|
|
379
|
+
// Remove: LC-176 bare ✕, no disc or fill, painted at nestedControl (29)
|
|
371
380
|
// with hitSlop restoring the 44 floor (data-mp-press-slop 8 → 29+16=45).
|
|
372
381
|
const remove = flist.querySelector('[aria-label="Remove hello.txt"]') as HTMLElement;
|
|
373
382
|
expect(remove).toBeTruthy();
|
|
374
383
|
expect(remove.getAttribute("data-nested-px")).toBe("29");
|
|
375
384
|
expect(remove.getAttribute("data-mp-press-slop")).toBe("8");
|
|
376
|
-
expect(remove.className).toMatch(/_bg-
|
|
377
|
-
|
|
385
|
+
expect(remove.className).toMatch(/_bg-transparent/);
|
|
386
|
+
expect(remove.className).not.toMatch(/_bg-color12/);
|
|
378
387
|
expect(remove.querySelector("svg")).toBeTruthy();
|
|
379
388
|
// The caption is dropped once files exist; the CTA stays.
|
|
380
389
|
expect(zone.textContent).not.toContain("Drag a file into this area");
|
|
@@ -4,9 +4,9 @@ import type { Meta, StoryObj } from "@storybook/react-native-web-vite";
|
|
|
4
4
|
import { useForm } from "@tanstack/react-form";
|
|
5
5
|
import { useEffect, useRef, useState } from "react";
|
|
6
6
|
import { YStack } from "tamagui";
|
|
7
|
-
import { Button } from "../../Button";
|
|
8
7
|
import { Form } from "../../Form";
|
|
9
8
|
import { FileUpload } from "./index";
|
|
9
|
+
import { ActionButton } from "@multiplatform.one/components";
|
|
10
10
|
|
|
11
11
|
const meta: Meta<typeof FileUpload> = {
|
|
12
12
|
title: "Forms/FileUpload",
|
|
@@ -80,7 +80,7 @@ export const Main: Story = {
|
|
|
80
80
|
helperText: "Upload multiple files",
|
|
81
81
|
} as any)}
|
|
82
82
|
/>
|
|
83
|
-
<
|
|
83
|
+
<ActionButton kind="submit" formAction="submit" />
|
|
84
84
|
</YStack>
|
|
85
85
|
</Form>
|
|
86
86
|
);
|