@jobber/components-native 0.29.0 → 0.31.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.
Files changed (78) hide show
  1. package/README.md +3 -0
  2. package/dist/src/FormField/FormField.js +10 -0
  3. package/dist/src/FormField/index.js +1 -0
  4. package/dist/src/Select/Select.js +79 -0
  5. package/dist/src/Select/Select.style.js +45 -0
  6. package/dist/src/Select/components/SelectDefaultPicker/SelectDefaultPicker.ios.js +30 -0
  7. package/dist/src/Select/components/SelectDefaultPicker/SelectDefaultPicker.js +16 -0
  8. package/dist/src/Select/components/SelectDefaultPicker/SelectDefaultPicker.style.js +29 -0
  9. package/dist/src/Select/components/SelectDefaultPicker/index.js +1 -0
  10. package/dist/src/Select/components/SelectDefaultPicker/messages.js +8 -0
  11. package/dist/src/Select/components/SelectIOSPicker/SelectIOSPicker.js +2 -0
  12. package/dist/src/Select/components/SelectIOSPicker/index.js +1 -0
  13. package/dist/src/Select/components/SelectInternalPicker/SelectInternalPicker.js +14 -0
  14. package/dist/src/Select/components/SelectInternalPicker/index.js +1 -0
  15. package/dist/src/Select/components/SelectInternalPicker/utils.js +13 -0
  16. package/dist/src/Select/components/SelectPressable/SelectPressable.js +15 -0
  17. package/dist/src/Select/components/SelectPressable/SelectPressable.style.js +7 -0
  18. package/dist/src/Select/components/SelectPressable/index.js +1 -0
  19. package/dist/src/Select/index.js +1 -0
  20. package/dist/src/Select/messages.js +13 -0
  21. package/dist/src/Select/types.js +1 -0
  22. package/dist/src/index.js +2 -0
  23. package/dist/tsconfig.tsbuildinfo +1 -1
  24. package/dist/types/src/FormField/FormField.d.ts +24 -0
  25. package/dist/types/src/FormField/index.d.ts +1 -0
  26. package/dist/types/src/Select/Select.d.ts +69 -0
  27. package/dist/types/src/Select/Select.style.d.ts +56 -0
  28. package/dist/types/src/Select/components/SelectDefaultPicker/SelectDefaultPicker.d.ts +5 -0
  29. package/dist/types/src/Select/components/SelectDefaultPicker/SelectDefaultPicker.ios.d.ts +5 -0
  30. package/dist/types/src/Select/components/SelectDefaultPicker/SelectDefaultPicker.style.d.ts +30 -0
  31. package/dist/types/src/Select/components/SelectDefaultPicker/index.d.ts +1 -0
  32. package/dist/types/src/Select/components/SelectDefaultPicker/messages.d.ts +7 -0
  33. package/dist/types/src/Select/components/SelectIOSPicker/SelectIOSPicker.d.ts +10 -0
  34. package/dist/types/src/Select/components/SelectIOSPicker/index.d.ts +1 -0
  35. package/dist/types/src/Select/components/SelectInternalPicker/SelectInternalPicker.d.ts +3 -0
  36. package/dist/types/src/Select/components/SelectInternalPicker/index.d.ts +1 -0
  37. package/dist/types/src/Select/components/SelectInternalPicker/utils.d.ts +3 -0
  38. package/dist/types/src/Select/components/SelectPressable/SelectPressable.d.ts +11 -0
  39. package/dist/types/src/Select/components/SelectPressable/SelectPressable.style.d.ts +5 -0
  40. package/dist/types/src/Select/components/SelectPressable/index.d.ts +1 -0
  41. package/dist/types/src/Select/index.d.ts +1 -0
  42. package/dist/types/src/Select/messages.d.ts +12 -0
  43. package/dist/types/src/Select/types.d.ts +38 -0
  44. package/dist/types/src/index.d.ts +2 -0
  45. package/ios/ComponentsNative-Bridging-Header.h +2 -0
  46. package/ios/ComponentsNative.xcodeproj/project.pbxproj +303 -0
  47. package/ios/Picker/ATLFallBackPickerView.swift +13 -0
  48. package/ios/Picker/ATLPickerOption.swift +44 -0
  49. package/ios/Picker/ATLPickerView.swift +61 -0
  50. package/ios/Picker/RCTATLPickerManager.m +26 -0
  51. package/ios/Picker/RCTATLPickerManager.swift +25 -0
  52. package/jobber-components-native.podspec +35 -0
  53. package/package.json +18 -3
  54. package/src/FormField/FormField.test.tsx +135 -0
  55. package/src/FormField/FormField.tsx +50 -0
  56. package/src/FormField/index.ts +1 -0
  57. package/src/Select/Select.style.ts +51 -0
  58. package/src/Select/Select.test.tsx +323 -0
  59. package/src/Select/Select.tsx +240 -0
  60. package/src/Select/components/SelectDefaultPicker/SelectDefaultPicker.ios.tsx +64 -0
  61. package/src/Select/components/SelectDefaultPicker/SelectDefaultPicker.style.ts +30 -0
  62. package/src/Select/components/SelectDefaultPicker/SelectDefaultPicker.test.tsx +76 -0
  63. package/src/Select/components/SelectDefaultPicker/SelectDefaultPicker.tsx +45 -0
  64. package/src/Select/components/SelectDefaultPicker/index.ts +1 -0
  65. package/src/Select/components/SelectDefaultPicker/messages.ts +9 -0
  66. package/src/Select/components/SelectIOSPicker/SelectIOSPicker.tsx +16 -0
  67. package/src/Select/components/SelectIOSPicker/index.ts +1 -0
  68. package/src/Select/components/SelectInternalPicker/SelectInternalPicker.test.tsx +100 -0
  69. package/src/Select/components/SelectInternalPicker/SelectInternalPicker.tsx +33 -0
  70. package/src/Select/components/SelectInternalPicker/index.ts +1 -0
  71. package/src/Select/components/SelectInternalPicker/utils.ts +20 -0
  72. package/src/Select/components/SelectPressable/SelectPressable.style.ts +8 -0
  73. package/src/Select/components/SelectPressable/SelectPressable.tsx +32 -0
  74. package/src/Select/components/SelectPressable/index.ts +1 -0
  75. package/src/Select/index.ts +1 -0
  76. package/src/Select/messages.ts +14 -0
  77. package/src/Select/types.ts +46 -0
  78. package/src/index.ts +2 -0
package/README.md CHANGED
@@ -15,6 +15,9 @@ run:
15
15
 
16
16
  ```sh
17
17
  npm install @jobber/components-native
18
+ # iOS Linking
19
+ cd ios
20
+ pod install
18
21
  ```
19
22
 
20
23
  ## Usage
@@ -0,0 +1,10 @@
1
+ import React from "react";
2
+ import { useFormController } from "../hooks";
3
+ export function FormField({ name, children, defaultValue: value, validations, }) {
4
+ const { error, field } = useFormController({
5
+ name,
6
+ value,
7
+ validations,
8
+ });
9
+ return React.createElement(React.Fragment, null, children(Object.assign({}, field), error));
10
+ }
@@ -0,0 +1 @@
1
+ export { FormField } from "./FormField";
@@ -0,0 +1,79 @@
1
+ import React from "react";
2
+ import { View } from "react-native";
3
+ import { useIntl } from "react-intl";
4
+ import { styles } from "./Select.style";
5
+ import { SelectInternalPicker } from "./components/SelectInternalPicker";
6
+ import { messages } from "./messages";
7
+ import { InputFieldWrapper } from "../InputFieldWrapper";
8
+ import { Icon } from "../Icon";
9
+ import { Text } from "../Text";
10
+ import { useFormController } from "../hooks";
11
+ export function Select({ value, defaultValue, onChange, children, placeholder, label, assistiveText, disabled, invalid, validations, accessibilityLabel, name, }) {
12
+ const { field, error } = useFormController({
13
+ name,
14
+ validations,
15
+ value: value !== null && value !== void 0 ? value : defaultValue,
16
+ });
17
+ const { formatMessage } = useIntl();
18
+ const internalValue = value !== null && value !== void 0 ? value : field.value;
19
+ const textVariation = getTextVariation({
20
+ disabled,
21
+ invalid: invalid || !!error,
22
+ });
23
+ const valueTextVariation = disabled ? "disabled" : undefined;
24
+ const hasValue = internalValue && (internalValue === null || internalValue === void 0 ? void 0 : internalValue.length) > 0;
25
+ return (React.createElement(InputFieldWrapper, { error: error, invalid: invalid || !!error, hasValue: hasValue, styleOverride: {
26
+ container: { borderBottomWidth: undefined },
27
+ } },
28
+ React.createElement(View, { testID: "ATL-Select", accessible: true, accessibilityLabel: getA11yLabel(), accessibilityValue: { text: getValue() }, accessibilityHint: formatMessage(messages.a11yHint), accessibilityRole: "button", accessibilityState: { disabled: disabled } },
29
+ React.createElement(SelectInternalPicker, { disabled: disabled, options: getOptions(), onChange: handleChange },
30
+ React.createElement(View, { style: [styles.container, (invalid || !!error) && styles.invalid] },
31
+ label && (React.createElement(Text, { level: "textSupporting", variation: textVariation, hideFromScreenReader: true }, label)),
32
+ React.createElement(View, { style: styles.input },
33
+ React.createElement(View, { style: styles.value },
34
+ React.createElement(Text, { variation: disabled ? "disabled" : "base", hideFromScreenReader: true }, getValue())),
35
+ React.createElement(View, { style: styles.icon },
36
+ React.createElement(Icon, { name: "arrowDown", color: valueTextVariation }))))),
37
+ assistiveText && (React.createElement(Text, { level: "textSupporting", variation: textVariation, hideFromScreenReader: true }, assistiveText)))));
38
+ function getA11yLabel() {
39
+ let text = [accessibilityLabel || label, assistiveText];
40
+ text = text.filter(Boolean);
41
+ return text.join(", ");
42
+ }
43
+ function handleChange(val) {
44
+ onChange === null || onChange === void 0 ? void 0 : onChange(val);
45
+ // need this onBlur for validations to occur
46
+ field.onBlur();
47
+ field.onChange(val);
48
+ }
49
+ function getOptions() {
50
+ const options = children.map(({ props }) => ({
51
+ label: props.children,
52
+ value: props.value,
53
+ isActive: props.value === internalValue,
54
+ }));
55
+ if (!internalValue || placeholder) {
56
+ options.unshift({
57
+ label: placeholder || formatMessage(messages.emptyValue),
58
+ value: "",
59
+ isActive: !internalValue,
60
+ });
61
+ }
62
+ return options;
63
+ }
64
+ function getValue() {
65
+ const options = getOptions();
66
+ const activeValue = options.find(option => option.isActive);
67
+ return ((activeValue === null || activeValue === void 0 ? void 0 : activeValue.label) || placeholder || formatMessage(messages.emptyValue));
68
+ }
69
+ }
70
+ function getTextVariation({ invalid, disabled, }) {
71
+ if (invalid)
72
+ return "error";
73
+ if (disabled)
74
+ return "disabled";
75
+ return "subdued";
76
+ }
77
+ export function Option({ children }) {
78
+ return React.createElement(React.Fragment, null, children);
79
+ }
@@ -0,0 +1,45 @@
1
+ import { StyleSheet } from "react-native";
2
+ import { commonInputStyles } from "../InputFieldWrapper";
3
+ import { tokens } from "../utils/design";
4
+ export const styles = StyleSheet.create({
5
+ container: StyleSheet.flatten([
6
+ commonInputStyles.container,
7
+ {
8
+ flexDirection: "column",
9
+ justifyContent: "flex-end",
10
+ minHeight: tokens["space-largest"] + tokens["border-base"],
11
+ },
12
+ ]),
13
+ input: StyleSheet.flatten([
14
+ commonInputStyles.input,
15
+ {
16
+ flexDirection: "row",
17
+ flexGrow: 0,
18
+ paddingBottom: tokens["space-smaller"],
19
+ minHeight: 0,
20
+ minWidth: "100%",
21
+ },
22
+ ]),
23
+ value: {
24
+ flexGrow: 1,
25
+ flexShrink: 1,
26
+ },
27
+ icon: {
28
+ flexGrow: 0,
29
+ flexShrink: 0,
30
+ },
31
+ invalid: {
32
+ color: tokens["color-critical"],
33
+ borderColor: tokens["color-critical"],
34
+ },
35
+ errorMessageWrapperIcon: {
36
+ flex: 0,
37
+ flexBasis: "auto",
38
+ paddingTop: tokens["space-minuscule"],
39
+ paddingRight: tokens["space-smaller"],
40
+ },
41
+ messageWrapper: {
42
+ paddingTop: tokens["space-smaller"],
43
+ flexDirection: "row",
44
+ },
45
+ });
@@ -0,0 +1,30 @@
1
+ import React, { useState } from "react";
2
+ import {
3
+ // Need to use iOS style button
4
+ Button, Modal, TouchableOpacity, View, } from "react-native";
5
+ import { Picker } from "@react-native-picker/picker";
6
+ import { SafeAreaView } from "react-native-safe-area-context";
7
+ import { useIntl } from "react-intl";
8
+ import { styles } from "./SelectDefaultPicker.style";
9
+ import { messages } from "./messages";
10
+ import { SelectPressable } from "../SelectPressable/SelectPressable";
11
+ export function SelectDefaultPicker({ children, options, onChange, }) {
12
+ const [show, setShow] = useState(false);
13
+ const { formatMessage } = useIntl();
14
+ const selectedLanguage = options.find(option => option.isActive);
15
+ return (React.createElement(React.Fragment, null,
16
+ React.createElement(SelectPressable, { onPress: showPicker }, children),
17
+ React.createElement(Modal, { visible: show, transparent: true, animationType: "slide", onRequestClose: hidePicker },
18
+ React.createElement(TouchableOpacity, { style: styles.overlay, onPress: hidePicker }),
19
+ React.createElement(View, { style: styles.actionBar },
20
+ React.createElement(Button, { title: formatMessage(messages.done), onPress: hidePicker })),
21
+ React.createElement(View, { style: styles.pickerContainer, testID: "select-wheel-picker" },
22
+ React.createElement(SafeAreaView, { edges: ["bottom"] },
23
+ React.createElement(Picker, { selectedValue: selectedLanguage === null || selectedLanguage === void 0 ? void 0 : selectedLanguage.value, onValueChange: onChange }, options.map(({ label, value }, i) => (React.createElement(Picker.Item, { key: i, label: label, value: value })))))))));
24
+ function showPicker() {
25
+ setShow(true);
26
+ }
27
+ function hidePicker() {
28
+ setShow(false);
29
+ }
30
+ }
@@ -0,0 +1,16 @@
1
+ import React, { useRef } from "react";
2
+ import { Picker } from "@react-native-picker/picker";
3
+ import { styles } from "./SelectDefaultPicker.style";
4
+ import { SelectPressable } from "../SelectPressable";
5
+ import { tokens } from "../../../utils/design";
6
+ export function SelectDefaultPicker({ children, options, disabled, onChange, }) {
7
+ var _a;
8
+ const selectedItem = options.find(option => option.isActive);
9
+ const pickerRef = useRef(null);
10
+ return (React.createElement(SelectPressable, { onPress: (_a = pickerRef.current) === null || _a === void 0 ? void 0 : _a.focus },
11
+ children,
12
+ React.createElement(Picker, { ref: pickerRef, selectedValue: selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.value, onValueChange: onChange, mode: "dropdown", enabled: !disabled, style: styles.androidPickerContainer }, options.map(({ label, value, isActive }, i) => (React.createElement(Picker.Item, { key: i, label: label, value: value, color: isSelected(isActive) }))))));
13
+ function isSelected(isActive) {
14
+ return isActive ? tokens["color-interactive"] : tokens["color-text"];
15
+ }
16
+ }
@@ -0,0 +1,29 @@
1
+ import { StyleSheet } from "react-native";
2
+ export const styles = StyleSheet.create({
3
+ overlay: { flex: 1 },
4
+ actionBar: {
5
+ flexDirection: "row",
6
+ height: 45,
7
+ justifyContent: "flex-end",
8
+ alignItems: "center",
9
+ paddingHorizontal: 10,
10
+ backgroundColor: "#f8f8f8",
11
+ borderTopWidth: 1,
12
+ borderTopColor: "#dedede",
13
+ zIndex: 2,
14
+ },
15
+ pickerContainer: {
16
+ justifyContent: "center",
17
+ backgroundColor: "#d0d4da",
18
+ },
19
+ androidPickerContainer: {
20
+ position: "absolute",
21
+ top: 0,
22
+ left: 0,
23
+ width: "100%",
24
+ height: "100%",
25
+ color: "transparent",
26
+ backgroundColor: "transparent",
27
+ opacity: 0,
28
+ },
29
+ });
@@ -0,0 +1 @@
1
+ export * from "./SelectDefaultPicker";
@@ -0,0 +1,8 @@
1
+ import { defineMessages } from "react-intl";
2
+ export const messages = defineMessages({
3
+ done: {
4
+ id: "done",
5
+ defaultMessage: "Done",
6
+ description: "Done action",
7
+ },
8
+ });
@@ -0,0 +1,2 @@
1
+ import { requireNativeComponent } from "react-native";
2
+ export const SelectIOSPicker = requireNativeComponent === null || requireNativeComponent === void 0 ? void 0 : requireNativeComponent("RCTATLPicker");
@@ -0,0 +1 @@
1
+ export * from "./SelectIOSPicker";
@@ -0,0 +1,14 @@
1
+ import React from "react";
2
+ import { handlePress, isIOS14AndUp } from "./utils";
3
+ import { SelectIOSPicker } from "../SelectIOSPicker";
4
+ import { SelectPressable } from "../SelectPressable";
5
+ import { SelectDefaultPicker } from "../SelectDefaultPicker";
6
+ export function SelectInternalPicker({ children, options, disabled, onChange, }) {
7
+ if (disabled)
8
+ return React.createElement(React.Fragment, null, children);
9
+ if (isIOS14AndUp()) {
10
+ return (React.createElement(SelectPressable, null,
11
+ React.createElement(SelectIOSPicker, { options: options, onOptionPress: handlePress(onChange) }, children)));
12
+ }
13
+ return (React.createElement(SelectDefaultPicker, { options: options, onChange: onChange }, children));
14
+ }
@@ -0,0 +1 @@
1
+ export * from "./SelectInternalPicker";
@@ -0,0 +1,13 @@
1
+ import { Platform } from "react-native";
2
+ export function handlePress(onChange) {
3
+ return ({ nativeEvent: { event } }) => {
4
+ return onChange(event);
5
+ };
6
+ }
7
+ export function isIOS14AndUp() {
8
+ if (Platform.OS === "ios") {
9
+ const majorVersionIOS = parseInt(Platform.Version, 10);
10
+ return majorVersionIOS >= 14;
11
+ }
12
+ return false;
13
+ }
@@ -0,0 +1,15 @@
1
+ import React from "react";
2
+ import { Keyboard, Pressable } from "react-native";
3
+ import { styles } from "./SelectPressable.style";
4
+ import { useIsScreenReaderEnabled } from "../../../hooks";
5
+ /**
6
+ * Switches between Pressable with pressed styling and a fragment when a
7
+ * screen-reader is being used to avoid screen-readers from ignoring the press
8
+ * on the MenuView
9
+ */
10
+ export function SelectPressable({ children, onPress, }) {
11
+ const isScreenReaderEnabled = useIsScreenReaderEnabled();
12
+ if (isScreenReaderEnabled)
13
+ return React.createElement(React.Fragment, null, children);
14
+ return (React.createElement(Pressable, { style: ({ pressed }) => [pressed && styles.pressed], onPressIn: Keyboard.dismiss, onPress: onPress }, children));
15
+ }
@@ -0,0 +1,7 @@
1
+ import { StyleSheet } from "react-native";
2
+ import { tokens } from "../../../utils/design";
3
+ export const styles = StyleSheet.create({
4
+ pressed: {
5
+ opacity: tokens["opacity-pressed"],
6
+ },
7
+ });
@@ -0,0 +1 @@
1
+ export * from "./SelectPressable";
@@ -0,0 +1 @@
1
+ export { Select, Option } from "./Select";
@@ -0,0 +1,13 @@
1
+ import { defineMessages } from "react-intl";
2
+ export const messages = defineMessages({
3
+ a11yHint: {
4
+ id: "a11yHint",
5
+ defaultMessage: "Select to open the picker",
6
+ description: "Accessibility hint on how to interact with the select",
7
+ },
8
+ emptyValue: {
9
+ id: "emptyValue",
10
+ defaultMessage: "Select an option",
11
+ description: "Accessibility hint on how to interact with the select",
12
+ },
13
+ });
@@ -0,0 +1 @@
1
+ export {};
package/dist/src/index.js CHANGED
@@ -12,6 +12,7 @@ export * from "./Divider";
12
12
  export * from "./EmptyState";
13
13
  export * from "./ErrorMessageWrapper";
14
14
  export * from "./Flex";
15
+ export * from "./FormField";
15
16
  export * from "./Heading";
16
17
  export * from "./Icon";
17
18
  export * from "./IconButton";
@@ -22,6 +23,7 @@ export * from "./InputSearch";
22
23
  export * from "./InputText";
23
24
  export * from "./TextList";
24
25
  export * from "./ProgressBar";
26
+ export * from "./Select";
25
27
  export * from "./StatusLabel";
26
28
  export * from "./Switch";
27
29
  export * from "./Text";