@jobber/components-native 0.9.0 → 0.10.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 (43) hide show
  1. package/dist/src/InputFieldWrapper/CommonInputStyles.style.js +33 -0
  2. package/dist/src/InputFieldWrapper/InputFieldWrapper.js +88 -0
  3. package/dist/src/InputFieldWrapper/InputFieldWrapper.style.js +79 -0
  4. package/dist/src/InputFieldWrapper/components/ClearAction/ClearAction.js +12 -0
  5. package/dist/src/InputFieldWrapper/components/ClearAction/ClearAction.style.js +25 -0
  6. package/dist/src/InputFieldWrapper/components/ClearAction/index.js +2 -0
  7. package/dist/src/InputFieldWrapper/components/ClearAction/messages.js +8 -0
  8. package/dist/src/InputFieldWrapper/components/Prefix/Prefix.js +34 -0
  9. package/dist/src/InputFieldWrapper/components/Suffix/Suffix.js +35 -0
  10. package/dist/src/InputFieldWrapper/hooks/useShowClear.js +15 -0
  11. package/dist/src/InputFieldWrapper/index.js +3 -0
  12. package/dist/src/index.js +1 -0
  13. package/dist/tsconfig.tsbuildinfo +1 -1
  14. package/dist/types/src/InputFieldWrapper/CommonInputStyles.style.d.ts +30 -0
  15. package/dist/types/src/InputFieldWrapper/InputFieldWrapper.d.ts +63 -0
  16. package/dist/types/src/InputFieldWrapper/InputFieldWrapper.style.d.ts +82 -0
  17. package/dist/types/src/InputFieldWrapper/components/ClearAction/ClearAction.d.ts +10 -0
  18. package/dist/types/src/InputFieldWrapper/components/ClearAction/ClearAction.style.d.ts +22 -0
  19. package/dist/types/src/InputFieldWrapper/components/ClearAction/index.d.ts +2 -0
  20. package/dist/types/src/InputFieldWrapper/components/ClearAction/messages.d.ts +7 -0
  21. package/dist/types/src/InputFieldWrapper/components/Prefix/Prefix.d.ts +23 -0
  22. package/dist/types/src/InputFieldWrapper/components/Suffix/Suffix.d.ts +25 -0
  23. package/dist/types/src/InputFieldWrapper/hooks/useShowClear.d.ts +10 -0
  24. package/dist/types/src/InputFieldWrapper/index.d.ts +4 -0
  25. package/dist/types/src/index.d.ts +1 -0
  26. package/package.json +4 -2
  27. package/src/InputFieldWrapper/CommonInputStyles.style.ts +37 -0
  28. package/src/InputFieldWrapper/InputFieldWrapper.style.ts +93 -0
  29. package/src/InputFieldWrapper/InputFieldWrapper.test.tsx +243 -0
  30. package/src/InputFieldWrapper/InputFieldWrapper.tsx +317 -0
  31. package/src/InputFieldWrapper/components/ClearAction/ClearAction.style.ts +27 -0
  32. package/src/InputFieldWrapper/components/ClearAction/ClearAction.test.tsx +15 -0
  33. package/src/InputFieldWrapper/components/ClearAction/ClearAction.tsx +32 -0
  34. package/src/InputFieldWrapper/components/ClearAction/index.ts +2 -0
  35. package/src/InputFieldWrapper/components/ClearAction/messages.ts +9 -0
  36. package/src/InputFieldWrapper/components/Prefix/Prefix.test.tsx +221 -0
  37. package/src/InputFieldWrapper/components/Prefix/Prefix.tsx +104 -0
  38. package/src/InputFieldWrapper/components/Suffix/Suffix.test.tsx +101 -0
  39. package/src/InputFieldWrapper/components/Suffix/Suffix.tsx +113 -0
  40. package/src/InputFieldWrapper/hooks/useShowClear.test.ts +158 -0
  41. package/src/InputFieldWrapper/hooks/useShowClear.ts +31 -0
  42. package/src/InputFieldWrapper/index.ts +4 -0
  43. package/src/index.ts +1 -0
@@ -0,0 +1,33 @@
1
+ import { StyleSheet } from "react-native";
2
+ import { typographyStyles } from "../Typography";
3
+ import { tokens } from "../utils/design";
4
+ export const commonInputStyles = StyleSheet.create({
5
+ input: {
6
+ width: "100%",
7
+ flexShrink: 1,
8
+ flexGrow: 1,
9
+ color: tokens["color-text"],
10
+ fontFamily: typographyStyles.baseRegularRegular.fontFamily,
11
+ fontSize: typographyStyles.defaultSize.fontSize,
12
+ letterSpacing: typographyStyles.baseLetterSpacing.letterSpacing,
13
+ minHeight: tokens["space-largest"],
14
+ padding: 0,
15
+ },
16
+ inputEmpty: {
17
+ paddingTop: 0,
18
+ },
19
+ inputDisabled: {
20
+ color: typographyStyles.disabled.color,
21
+ },
22
+ container: {
23
+ marginVertical: tokens["space-smaller"],
24
+ backgroundColor: tokens["color-surface"],
25
+ minHeight: tokens["space-largest"],
26
+ flexDirection: "row",
27
+ justifyContent: "space-between",
28
+ width: "100%",
29
+ borderColor: tokens["color-grey"],
30
+ borderStyle: "solid",
31
+ borderBottomWidth: tokens["border-base"],
32
+ },
33
+ });
@@ -0,0 +1,88 @@
1
+ import React from "react";
2
+ import {
3
+ // eslint-disable-next-line no-restricted-imports
4
+ Text as RNText, View, } from "react-native";
5
+ import { styles } from "./InputFieldWrapper.style";
6
+ import { PrefixIcon, PrefixLabel } from "./components/Prefix/Prefix";
7
+ import { SuffixIcon, SuffixLabel } from "./components/Suffix/Suffix";
8
+ import { ClearAction } from "./components/ClearAction";
9
+ import { ErrorMessageWrapper } from "../ErrorMessageWrapper";
10
+ import { typographyStyles } from "../Typography";
11
+ import { Text } from "../Text";
12
+ export function InputFieldWrapper({ invalid, disabled, placeholder, assistiveText, prefix, suffix, hasMiniLabel = false, hasValue = false, error, focused = false, children, onClear, showClearAction = false, styleOverride, }) {
13
+ fieldAffixRequiredPropsCheck([prefix, suffix]);
14
+ const handleClear = onClear !== null && onClear !== void 0 ? onClear : noopClear;
15
+ warnIfClearActionWithNoOnClear(onClear, showClearAction);
16
+ const inputInvalid = Boolean(invalid) || Boolean(error);
17
+ return (React.createElement(ErrorMessageWrapper, { message: getMessage({ invalid, error }) },
18
+ React.createElement(View, { testID: "ATL-InputFieldWrapper", style: [
19
+ styles.container,
20
+ focused && styles.inputFocused,
21
+ (Boolean(invalid) || error) && styles.inputInvalid,
22
+ disabled && styles.disabled,
23
+ styleOverride === null || styleOverride === void 0 ? void 0 : styleOverride.container,
24
+ ] },
25
+ (prefix === null || prefix === void 0 ? void 0 : prefix.icon) && (React.createElement(PrefixIcon, { disabled: disabled, focused: focused, hasMiniLabel: hasMiniLabel, inputInvalid: inputInvalid, icon: prefix.icon })),
26
+ React.createElement(View, { style: [styles.inputContainer] },
27
+ React.createElement(View, { style: [
28
+ !!placeholder && styles.label,
29
+ hasMiniLabel && styles.miniLabel,
30
+ disabled && styles.disabled,
31
+ hasMiniLabel &&
32
+ showClearAction &&
33
+ styles.miniLabelShowClearAction,
34
+ ], pointerEvents: "none" },
35
+ React.createElement(Placeholder, { placeholder: placeholder, labelVariation: getLabelVariation(error, invalid, focused, disabled), hasMiniLabel: hasMiniLabel, styleOverride: styleOverride === null || styleOverride === void 0 ? void 0 : styleOverride.placeholderText })),
36
+ (prefix === null || prefix === void 0 ? void 0 : prefix.label) && hasValue && (React.createElement(PrefixLabel, { disabled: disabled, focused: focused, hasMiniLabel: hasMiniLabel, inputInvalid: inputInvalid, label: prefix.label, styleOverride: styleOverride === null || styleOverride === void 0 ? void 0 : styleOverride.prefixLabel })),
37
+ children,
38
+ (showClearAction || (suffix === null || suffix === void 0 ? void 0 : suffix.label) || (suffix === null || suffix === void 0 ? void 0 : suffix.icon)) && (React.createElement(View, { style: styles.inputEndContainer },
39
+ showClearAction && (React.createElement(ClearAction, { hasMarginRight: !!(suffix === null || suffix === void 0 ? void 0 : suffix.icon) || !!(suffix === null || suffix === void 0 ? void 0 : suffix.label), onPress: handleClear })),
40
+ (suffix === null || suffix === void 0 ? void 0 : suffix.label) && hasValue && (React.createElement(SuffixLabel, { disabled: disabled, focused: focused, hasMiniLabel: hasMiniLabel, inputInvalid: inputInvalid, label: suffix.label, hasLeftMargin: !showClearAction, styleOverride: styleOverride === null || styleOverride === void 0 ? void 0 : styleOverride.suffixLabel })),
41
+ (suffix === null || suffix === void 0 ? void 0 : suffix.icon) && (React.createElement(SuffixIcon, { disabled: disabled, focused: focused, hasMiniLabel: hasMiniLabel, hasLeftMargin: !!(!showClearAction || (suffix === null || suffix === void 0 ? void 0 : suffix.label)), inputInvalid: inputInvalid, icon: suffix.icon, onPress: suffix.onPress })))))),
42
+ assistiveText && !error && !invalid && (React.createElement(Text, { level: "textSupporting", variation: disabled ? "disabled" : focused ? "interactive" : "subdued" }, assistiveText))));
43
+ }
44
+ function getLabelVariation(error, invalid, focused, disabled) {
45
+ if (invalid || error) {
46
+ return "error";
47
+ }
48
+ else if (disabled) {
49
+ return "disabled";
50
+ }
51
+ else if (focused) {
52
+ return "interactive";
53
+ }
54
+ return "subdued";
55
+ }
56
+ function fieldAffixRequiredPropsCheck(affixPair) {
57
+ affixPair.map(affix => {
58
+ if (typeof affix !== "undefined" && !affix.icon && !affix.label) {
59
+ throw new Error(`One of 'label' or 'icon' is required by the field affix component.`);
60
+ }
61
+ });
62
+ }
63
+ function warnIfClearActionWithNoOnClear(onClear, showClearAction) {
64
+ if (showClearAction && !onClear && __DEV__) {
65
+ console.warn("Declare an `onClear` prop on your input. You can set `clearable` to never or `showClearAction` to false if you don't need a clearable input");
66
+ }
67
+ }
68
+ function noopClear() {
69
+ warnIfClearActionWithNoOnClear(undefined, true);
70
+ }
71
+ function getMessage({ invalid, error, }) {
72
+ const messages = [];
73
+ if (error === null || error === void 0 ? void 0 : error.message)
74
+ messages.push(error.message);
75
+ if (invalid && typeof invalid === "string")
76
+ messages.push(invalid);
77
+ return messages.join("\n");
78
+ }
79
+ function Placeholder({ placeholder, styleOverride, labelVariation, hasMiniLabel, }) {
80
+ return (React.createElement(React.Fragment, null, !styleOverride ? (React.createElement(Text, { hideFromScreenReader: true, maxLines: "single", variation: labelVariation, level: hasMiniLabel ? "textSupporting" : "text" }, placeholder)) : (React.createElement(RNText, { accessibilityRole: "none", accessible: false, importantForAccessibility: "no-hide-descendants", numberOfLines: 1, style: [
81
+ typographyStyles[labelVariation],
82
+ typographyStyles.baseRegularRegular,
83
+ hasMiniLabel
84
+ ? typographyStyles.smallSize
85
+ : typographyStyles.defaultSize,
86
+ styleOverride,
87
+ ] }, placeholder))));
88
+ }
@@ -0,0 +1,79 @@
1
+ import { StyleSheet } from "react-native";
2
+ import { commonInputStyles } from "./CommonInputStyles.style";
3
+ import { tokens } from "../utils/design";
4
+ import { typographyStyles } from "../Typography";
5
+ export const styles = StyleSheet.create({
6
+ container: StyleSheet.flatten([commonInputStyles.container]),
7
+ inputContainer: {
8
+ flexDirection: "row",
9
+ flex: 1,
10
+ justifyContent: "flex-start",
11
+ },
12
+ inputFocused: {
13
+ borderColor: tokens["color-interactive"],
14
+ },
15
+ inputInvalid: {
16
+ borderColor: tokens["color-critical"],
17
+ },
18
+ label: {
19
+ // for placeholder
20
+ position: "absolute",
21
+ top: typographyStyles.smallSize.fontSize,
22
+ right: 0,
23
+ bottom: 0,
24
+ left: 0,
25
+ },
26
+ miniLabel: {
27
+ top: 0,
28
+ paddingTop: tokens["space-smallest"],
29
+ backgroundColor: tokens["color-surface"],
30
+ maxHeight: (typographyStyles.smallSize.lineHeight || 0) + tokens["space-smaller"],
31
+ zIndex: 1,
32
+ },
33
+ // Prevents the miniLabel from cutting off the ClearAction button
34
+ miniLabelShowClearAction: {
35
+ backgroundColor: "transparent",
36
+ },
37
+ disabled: {
38
+ backgroundColor: tokens["color-disabled--secondary"],
39
+ borderTopLeftRadius: tokens["radius-large"],
40
+ borderTopRightRadius: tokens["radius-large"],
41
+ },
42
+ fieldAffix: {
43
+ flexDirection: "row",
44
+ },
45
+ fieldAffixMiniLabel: {
46
+ paddingTop: 0,
47
+ // @ts-expect-error tsc-ci
48
+ top: typographyStyles.smallSize.fontSize / 2,
49
+ right: 0,
50
+ bottom: 0,
51
+ left: 0,
52
+ },
53
+ prefixIcon: {
54
+ justifyContent: "center",
55
+ paddingRight: tokens["space-small"],
56
+ },
57
+ prefixLabel: {
58
+ justifyContent: "center",
59
+ paddingTop: tokens["space-minuscule"],
60
+ paddingRight: tokens["space-minuscule"],
61
+ },
62
+ suffixIcon: {
63
+ justifyContent: "center",
64
+ },
65
+ suffixLabel: {
66
+ justifyContent: "center",
67
+ paddingTop: tokens["space-minuscule"],
68
+ },
69
+ suffixIconMargin: {
70
+ marginLeft: tokens["space-small"] + tokens["space-smaller"],
71
+ },
72
+ suffixLabelMargin: {
73
+ marginLeft: tokens["space-smallest"],
74
+ },
75
+ inputEndContainer: {
76
+ flexDirection: "row",
77
+ zIndex: 1,
78
+ },
79
+ });
@@ -0,0 +1,12 @@
1
+ import React from "react";
2
+ import { Pressable, View } from "react-native";
3
+ import { useIntl } from "react-intl";
4
+ import { styles } from "./ClearAction.style";
5
+ import { messages } from "./messages";
6
+ import { Icon } from "../../../Icon";
7
+ export function ClearAction({ onPress, hasMarginRight = false, }) {
8
+ const { formatMessage } = useIntl();
9
+ return (React.createElement(Pressable, { style: [styles.container, hasMarginRight && styles.addedMargin], onPress: onPress, accessibilityLabel: formatMessage(messages.clearTextLabel) },
10
+ React.createElement(View, { style: styles.circle },
11
+ React.createElement(Icon, { size: "small", name: "cross", color: "interactiveSubtle" }))));
12
+ }
@@ -0,0 +1,25 @@
1
+ import { StyleSheet } from "react-native";
2
+ import { tokens } from "../../../utils/design";
3
+ const width = tokens["space-smaller"] + tokens["space-larger"];
4
+ export const styles = StyleSheet.create({
5
+ container: {
6
+ width,
7
+ height: "100%",
8
+ flexDirection: "row",
9
+ justifyContent: "center",
10
+ alignItems: "center",
11
+ alignSelf: "center",
12
+ },
13
+ circle: {
14
+ backgroundColor: tokens["color-surface--background"],
15
+ borderRadius: tokens["radius-circle"],
16
+ width: tokens["space-large"],
17
+ height: tokens["space-large"],
18
+ flexDirection: "row",
19
+ justifyContent: "center",
20
+ alignItems: "center",
21
+ },
22
+ addedMargin: {
23
+ marginRight: tokens["space-small"],
24
+ },
25
+ });
@@ -0,0 +1,2 @@
1
+ export { ClearAction } from "./ClearAction";
2
+ export { messages } from "./messages";
@@ -0,0 +1,8 @@
1
+ import { defineMessages } from "react-intl";
2
+ export const messages = defineMessages({
3
+ clearTextLabel: {
4
+ id: "clearTextLabel",
5
+ defaultMessage: "Clear input",
6
+ description: "Accessiblity label for the clear input button",
7
+ },
8
+ });
@@ -0,0 +1,34 @@
1
+ import React from "react";
2
+ import {
3
+ // eslint-disable-next-line no-restricted-imports
4
+ Text as RNText, View, } from "react-native";
5
+ import { Icon } from "../../../Icon";
6
+ import { Text } from "../../../Text";
7
+ import { tokens } from "../../../utils/design";
8
+ import { typographyStyles } from "../../../Typography";
9
+ import { styles } from "../../InputFieldWrapper.style";
10
+ export const prefixLabelTestId = "ATL-InputFieldWrapper-PrefixLabel";
11
+ export const prefixIconTestId = "ATL-InputFieldWrapper-PrefixIcon";
12
+ export function PrefixLabel({ focused, disabled, hasMiniLabel, inputInvalid, label, styleOverride, }) {
13
+ return (React.createElement(View, { style: [
14
+ styles.fieldAffix,
15
+ focused && styles.inputFocused,
16
+ inputInvalid && styles.inputInvalid,
17
+ ], testID: prefixLabelTestId },
18
+ React.createElement(View, { style: [styles.prefixLabel, hasMiniLabel && styles.fieldAffixMiniLabel] }, !styleOverride ? (React.createElement(Text, { variation: disabled ? "disabled" : "base" }, label)) : (React.createElement(RNText, { allowFontScaling: true, style: [
19
+ typographyStyles.baseRegularRegular,
20
+ typographyStyles.base,
21
+ typographyStyles.defaultSize,
22
+ disabled ? typographyStyles.subdued : typographyStyles.base,
23
+ styleOverride,
24
+ ] }, label)))));
25
+ }
26
+ export function PrefixIcon({ focused, disabled, inputInvalid, icon, }) {
27
+ return (React.createElement(View, { testID: prefixIconTestId, style: [
28
+ styles.fieldAffix,
29
+ focused && styles.inputFocused,
30
+ inputInvalid && styles.inputInvalid,
31
+ ] },
32
+ React.createElement(View, { style: styles.prefixIcon },
33
+ React.createElement(Icon, { customColor: disabled ? tokens["color-disabled"] : tokens["color-greyBlue"], name: icon }))));
34
+ }
@@ -0,0 +1,35 @@
1
+ import React from "react";
2
+ import { Pressable,
3
+ // eslint-disable-next-line no-restricted-imports
4
+ Text as RNText, View, } from "react-native";
5
+ import { tokens } from "../../../utils/design";
6
+ import { Icon } from "../../../Icon";
7
+ import { Text } from "../../../Text";
8
+ import { typographyStyles } from "../../../Typography";
9
+ import { styles } from "../../InputFieldWrapper.style";
10
+ export const suffixLabelTestId = "ATL-InputFieldWrapper-SuffixLabel";
11
+ export const suffixIconTestId = "ATL-InputFieldWrapper-SuffixIcon";
12
+ export function SuffixLabel({ focused, disabled, hasMiniLabel, inputInvalid, label, hasLeftMargin = true, styleOverride, }) {
13
+ return (React.createElement(View, { testID: suffixLabelTestId, style: [
14
+ styles.fieldAffix,
15
+ focused && styles.inputFocused,
16
+ inputInvalid && styles.inputInvalid,
17
+ hasLeftMargin && styles.suffixLabelMargin,
18
+ ] },
19
+ React.createElement(View, { style: [styles.suffixLabel, hasMiniLabel && styles.fieldAffixMiniLabel] }, !styleOverride ? (React.createElement(Text, { variation: disabled ? "disabled" : "base" }, label)) : (React.createElement(RNText, { allowFontScaling: true, style: [
20
+ typographyStyles.baseRegularRegular,
21
+ typographyStyles.base,
22
+ typographyStyles.defaultSize,
23
+ disabled ? typographyStyles.subdued : typographyStyles.base,
24
+ styleOverride,
25
+ ] }, label)))));
26
+ }
27
+ export function SuffixIcon({ focused, disabled, inputInvalid, icon, hasLeftMargin = false, onPress, }) {
28
+ return (React.createElement(View, { testID: suffixIconTestId, style: [
29
+ styles.fieldAffix,
30
+ focused && styles.inputFocused,
31
+ inputInvalid && styles.inputInvalid,
32
+ ] },
33
+ React.createElement(Pressable, { style: [styles.suffixIcon, hasLeftMargin && styles.suffixIconMargin], onPress: onPress },
34
+ React.createElement(Icon, { customColor: disabled ? tokens["color-disabled"] : tokens["color-greyBlue"], name: icon }))));
35
+ }
@@ -0,0 +1,15 @@
1
+ export function useShowClear({ clearable, multiline, focused, hasValue, disabled = false, }) {
2
+ if (multiline && clearable !== "never") {
3
+ throw new Error("Multiline inputs can not be clearable");
4
+ }
5
+ // Do not show if there is no value
6
+ if (!hasValue || clearable === "never" || disabled) {
7
+ return false;
8
+ }
9
+ switch (clearable) {
10
+ case "while-editing":
11
+ return focused;
12
+ case "always":
13
+ return true;
14
+ }
15
+ }
@@ -0,0 +1,3 @@
1
+ export { commonInputStyles } from "./CommonInputStyles.style";
2
+ export { InputFieldWrapper } from "./InputFieldWrapper";
3
+ export { useShowClear } from "./hooks/useShowClear";
package/dist/src/index.js CHANGED
@@ -9,3 +9,4 @@ export * from "./ActivityIndicator";
9
9
  export * from "./Card";
10
10
  export * from "./StatusLabel";
11
11
  export * from "./AtlantisContext";
12
+ export * from "./InputFieldWrapper";