@khanacademy/wonder-blocks-form 4.8.1 → 4.9.1
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/CHANGELOG.md +28 -0
- package/dist/components/field-heading.d.ts +4 -0
- package/dist/components/text-area.d.ts +7 -1
- package/dist/components/text-field.d.ts +1 -4
- package/dist/es/index.js +110 -45
- package/dist/index.js +109 -44
- package/package.json +7 -7
- package/src/components/__tests__/labeled-text-field.test.tsx +0 -23
- package/src/components/field-heading.tsx +26 -7
- package/src/components/labeled-text-field.tsx +1 -0
- package/src/components/text-area.tsx +28 -8
- package/src/components/text-field.tsx +82 -44
- package/tsconfig-build.tsbuildinfo +1 -1
package/dist/index.js
CHANGED
|
@@ -622,15 +622,14 @@ const RadioGroup = React__namespace.forwardRef(function RadioGroup(props, ref) {
|
|
|
622
622
|
})));
|
|
623
623
|
});
|
|
624
624
|
|
|
625
|
-
const _excluded$2 = ["id", "type", "value", "name", "disabled", "onKeyDown", "placeholder", "
|
|
625
|
+
const _excluded$2 = ["id", "type", "value", "name", "disabled", "onKeyDown", "placeholder", "style", "testId", "readOnly", "autoFocus", "autoComplete", "forwardedRef", "light", "onFocus", "onBlur", "onValidate", "validate", "onChange", "required"];
|
|
626
626
|
const defaultErrorMessage$1 = "This field is required.";
|
|
627
627
|
const StyledInput = wonderBlocksCore.addStyle("input");
|
|
628
628
|
class TextField extends React__namespace.Component {
|
|
629
629
|
constructor(props) {
|
|
630
630
|
super(props);
|
|
631
631
|
this.state = {
|
|
632
|
-
error: null
|
|
633
|
-
focused: false
|
|
632
|
+
error: null
|
|
634
633
|
};
|
|
635
634
|
this.maybeValidate = newValue => {
|
|
636
635
|
const {
|
|
@@ -671,25 +670,30 @@ class TextField extends React__namespace.Component {
|
|
|
671
670
|
const {
|
|
672
671
|
onFocus
|
|
673
672
|
} = this.props;
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
}
|
|
677
|
-
if (onFocus) {
|
|
678
|
-
onFocus(event);
|
|
679
|
-
}
|
|
680
|
-
});
|
|
673
|
+
if (onFocus) {
|
|
674
|
+
onFocus(event);
|
|
675
|
+
}
|
|
681
676
|
};
|
|
682
677
|
this.handleBlur = event => {
|
|
683
678
|
const {
|
|
684
679
|
onBlur
|
|
685
680
|
} = this.props;
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
681
|
+
if (onBlur) {
|
|
682
|
+
onBlur(event);
|
|
683
|
+
}
|
|
684
|
+
};
|
|
685
|
+
this.getStyles = () => {
|
|
686
|
+
const {
|
|
687
|
+
disabled,
|
|
688
|
+
light
|
|
689
|
+
} = this.props;
|
|
690
|
+
const {
|
|
691
|
+
error
|
|
692
|
+
} = this.state;
|
|
693
|
+
const baseStyles = [styles$2.input, wonderBlocksTypography.styles.LabelMedium];
|
|
694
|
+
const defaultStyles = [styles$2.default, !disabled && styles$2.defaultFocus, disabled && styles$2.disabled, !!error && styles$2.error];
|
|
695
|
+
const lightStyles = [styles$2.light, !disabled && styles$2.lightFocus, disabled && styles$2.lightDisabled, !!error && styles$2.lightError];
|
|
696
|
+
return [...baseStyles, ...(light ? lightStyles : defaultStyles)];
|
|
693
697
|
};
|
|
694
698
|
if (props.validate && props.value !== "") {
|
|
695
699
|
this.state.error = props.validate(props.value) || null;
|
|
@@ -710,7 +714,6 @@ class TextField extends React__namespace.Component {
|
|
|
710
714
|
disabled,
|
|
711
715
|
onKeyDown,
|
|
712
716
|
placeholder,
|
|
713
|
-
light,
|
|
714
717
|
style,
|
|
715
718
|
testId,
|
|
716
719
|
readOnly,
|
|
@@ -723,7 +726,7 @@ class TextField extends React__namespace.Component {
|
|
|
723
726
|
id: id,
|
|
724
727
|
scope: "text-field"
|
|
725
728
|
}, uniqueId => React__namespace.createElement(StyledInput, _extends__default["default"]({
|
|
726
|
-
style: [
|
|
729
|
+
style: [this.getStyles(), style],
|
|
727
730
|
id: uniqueId,
|
|
728
731
|
type: type,
|
|
729
732
|
placeholder: placeholder,
|
|
@@ -753,12 +756,10 @@ const styles$2 = aphrodite.StyleSheet.create({
|
|
|
753
756
|
input: {
|
|
754
757
|
width: "100%",
|
|
755
758
|
height: 40,
|
|
756
|
-
borderRadius:
|
|
759
|
+
borderRadius: wonderBlocksTokens.border.radius.medium_4,
|
|
757
760
|
boxSizing: "border-box",
|
|
758
761
|
paddingLeft: wonderBlocksTokens.spacing.medium_16,
|
|
759
|
-
margin: 0
|
|
760
|
-
outline: "none",
|
|
761
|
-
boxShadow: "none"
|
|
762
|
+
margin: 0
|
|
762
763
|
},
|
|
763
764
|
default: {
|
|
764
765
|
background: wonderBlocksTokens.color.white,
|
|
@@ -768,12 +769,23 @@ const styles$2 = aphrodite.StyleSheet.create({
|
|
|
768
769
|
color: wonderBlocksTokens.color.offBlack64
|
|
769
770
|
}
|
|
770
771
|
},
|
|
772
|
+
defaultFocus: {
|
|
773
|
+
":focus-visible": {
|
|
774
|
+
borderColor: wonderBlocksTokens.color.blue,
|
|
775
|
+
outline: `1px solid ${wonderBlocksTokens.color.blue}`,
|
|
776
|
+
outlineOffset: 0
|
|
777
|
+
}
|
|
778
|
+
},
|
|
771
779
|
error: {
|
|
772
780
|
background: wonderBlocksTokens.color.fadedRed8,
|
|
773
781
|
border: `1px solid ${wonderBlocksTokens.color.red}`,
|
|
774
782
|
color: wonderBlocksTokens.color.offBlack,
|
|
775
783
|
"::placeholder": {
|
|
776
784
|
color: wonderBlocksTokens.color.offBlack64
|
|
785
|
+
},
|
|
786
|
+
":focus-visible": {
|
|
787
|
+
outlineColor: wonderBlocksTokens.color.red,
|
|
788
|
+
borderColor: wonderBlocksTokens.color.red
|
|
777
789
|
}
|
|
778
790
|
},
|
|
779
791
|
disabled: {
|
|
@@ -781,22 +793,57 @@ const styles$2 = aphrodite.StyleSheet.create({
|
|
|
781
793
|
border: `1px solid ${wonderBlocksTokens.color.offBlack16}`,
|
|
782
794
|
color: wonderBlocksTokens.color.offBlack64,
|
|
783
795
|
"::placeholder": {
|
|
784
|
-
color: wonderBlocksTokens.color.
|
|
796
|
+
color: wonderBlocksTokens.color.offBlack64
|
|
797
|
+
},
|
|
798
|
+
cursor: "not-allowed",
|
|
799
|
+
":focus-visible": {
|
|
800
|
+
outline: "none",
|
|
801
|
+
boxShadow: `0 0 0 1px ${wonderBlocksTokens.color.white}, 0 0 0 3px ${wonderBlocksTokens.color.offBlack32}`
|
|
785
802
|
}
|
|
786
803
|
},
|
|
787
|
-
|
|
804
|
+
light: {
|
|
788
805
|
background: wonderBlocksTokens.color.white,
|
|
789
|
-
border: `1px solid ${wonderBlocksTokens.color.
|
|
806
|
+
border: `1px solid ${wonderBlocksTokens.color.offBlack16}`,
|
|
790
807
|
color: wonderBlocksTokens.color.offBlack,
|
|
791
808
|
"::placeholder": {
|
|
792
809
|
color: wonderBlocksTokens.color.offBlack64
|
|
793
810
|
}
|
|
794
811
|
},
|
|
795
|
-
|
|
796
|
-
|
|
812
|
+
lightFocus: {
|
|
813
|
+
":focus-visible": {
|
|
814
|
+
outline: `1px solid ${wonderBlocksTokens.color.blue}`,
|
|
815
|
+
outlineOffset: 0,
|
|
816
|
+
borderColor: wonderBlocksTokens.color.blue,
|
|
817
|
+
boxShadow: `0px 0px 0px 2px ${wonderBlocksTokens.color.blue}, 0px 0px 0px 3px ${wonderBlocksTokens.color.white}`
|
|
818
|
+
}
|
|
819
|
+
},
|
|
820
|
+
lightDisabled: {
|
|
821
|
+
backgroundColor: "transparent",
|
|
822
|
+
border: `1px solid ${wonderBlocksTokens.color.white32}`,
|
|
823
|
+
color: wonderBlocksTokens.color.white64,
|
|
824
|
+
"::placeholder": {
|
|
825
|
+
color: wonderBlocksTokens.color.white64
|
|
826
|
+
},
|
|
827
|
+
cursor: "not-allowed",
|
|
828
|
+
":focus-visible": {
|
|
829
|
+
borderColor: wonderBlocksTokens.mix(wonderBlocksTokens.color.white32, wonderBlocksTokens.color.blue),
|
|
830
|
+
outline: "none",
|
|
831
|
+
boxShadow: `0 0 0 1px ${wonderBlocksTokens.color.offBlack32}, 0 0 0 3px ${wonderBlocksTokens.color.fadedBlue}`
|
|
832
|
+
}
|
|
797
833
|
},
|
|
798
|
-
|
|
799
|
-
|
|
834
|
+
lightError: {
|
|
835
|
+
background: wonderBlocksTokens.color.fadedRed8,
|
|
836
|
+
border: `1px solid ${wonderBlocksTokens.color.red}`,
|
|
837
|
+
boxShadow: `0px 0px 0px 1px ${wonderBlocksTokens.color.red}, 0px 0px 0px 2px ${wonderBlocksTokens.color.white}`,
|
|
838
|
+
color: wonderBlocksTokens.color.offBlack,
|
|
839
|
+
"::placeholder": {
|
|
840
|
+
color: wonderBlocksTokens.color.offBlack64
|
|
841
|
+
},
|
|
842
|
+
":focus-visible": {
|
|
843
|
+
outlineColor: wonderBlocksTokens.color.red,
|
|
844
|
+
borderColor: wonderBlocksTokens.color.red,
|
|
845
|
+
boxShadow: `0px 0px 0px 2px ${wonderBlocksTokens.color.red}, 0px 0px 0px 3px ${wonderBlocksTokens.color.white}`
|
|
846
|
+
}
|
|
800
847
|
}
|
|
801
848
|
});
|
|
802
849
|
var TextField$1 = React__namespace.forwardRef((props, ref) => React__namespace.createElement(TextField, _extends__default["default"]({}, props, {
|
|
@@ -810,14 +857,15 @@ class FieldHeading extends React__namespace.Component {
|
|
|
810
857
|
label,
|
|
811
858
|
id,
|
|
812
859
|
required,
|
|
813
|
-
testId
|
|
860
|
+
testId,
|
|
861
|
+
light
|
|
814
862
|
} = this.props;
|
|
815
863
|
const requiredIcon = React__namespace.createElement(StyledSpan, {
|
|
816
|
-
style: styles$1.required,
|
|
864
|
+
style: light ? styles$1.lightRequired : styles$1.required,
|
|
817
865
|
"aria-hidden": true
|
|
818
866
|
}, " ", "*");
|
|
819
867
|
return React__namespace.createElement(React__namespace.Fragment, null, React__namespace.createElement(wonderBlocksTypography.LabelMedium, {
|
|
820
|
-
style: styles$1.label,
|
|
868
|
+
style: light ? styles$1.lightLabel : styles$1.label,
|
|
821
869
|
tag: "label",
|
|
822
870
|
htmlFor: id && `${id}-field`,
|
|
823
871
|
testId: testId && `${testId}-label`
|
|
@@ -828,13 +876,14 @@ class FieldHeading extends React__namespace.Component {
|
|
|
828
876
|
maybeRenderDescription() {
|
|
829
877
|
const {
|
|
830
878
|
description,
|
|
831
|
-
testId
|
|
879
|
+
testId,
|
|
880
|
+
light
|
|
832
881
|
} = this.props;
|
|
833
882
|
if (!description) {
|
|
834
883
|
return null;
|
|
835
884
|
}
|
|
836
885
|
return React__namespace.createElement(React__namespace.Fragment, null, React__namespace.createElement(wonderBlocksTypography.LabelSmall, {
|
|
837
|
-
style: styles$1.description,
|
|
886
|
+
style: light ? styles$1.lightDescription : styles$1.description,
|
|
838
887
|
testId: testId && `${testId}-description`
|
|
839
888
|
}, description), React__namespace.createElement(wonderBlocksLayout.Strut, {
|
|
840
889
|
size: wonderBlocksTokens.spacing.xxxSmall_4
|
|
@@ -844,7 +893,8 @@ class FieldHeading extends React__namespace.Component {
|
|
|
844
893
|
const {
|
|
845
894
|
error,
|
|
846
895
|
id,
|
|
847
|
-
testId
|
|
896
|
+
testId,
|
|
897
|
+
light
|
|
848
898
|
} = this.props;
|
|
849
899
|
if (!error) {
|
|
850
900
|
return null;
|
|
@@ -852,7 +902,7 @@ class FieldHeading extends React__namespace.Component {
|
|
|
852
902
|
return React__namespace.createElement(React__namespace.Fragment, null, React__namespace.createElement(wonderBlocksLayout.Strut, {
|
|
853
903
|
size: wonderBlocksTokens.spacing.small_12
|
|
854
904
|
}), React__namespace.createElement(wonderBlocksTypography.LabelSmall, {
|
|
855
|
-
style: styles$1.error,
|
|
905
|
+
style: light ? styles$1.lightError : styles$1.error,
|
|
856
906
|
role: "alert",
|
|
857
907
|
id: id && `${id}-error`,
|
|
858
908
|
testId: testId && `${testId}-error`
|
|
@@ -874,14 +924,26 @@ const styles$1 = aphrodite.StyleSheet.create({
|
|
|
874
924
|
label: {
|
|
875
925
|
color: wonderBlocksTokens.color.offBlack
|
|
876
926
|
},
|
|
927
|
+
lightLabel: {
|
|
928
|
+
color: wonderBlocksTokens.color.white
|
|
929
|
+
},
|
|
877
930
|
description: {
|
|
878
931
|
color: wonderBlocksTokens.color.offBlack64
|
|
879
932
|
},
|
|
933
|
+
lightDescription: {
|
|
934
|
+
color: wonderBlocksTokens.color.white64
|
|
935
|
+
},
|
|
880
936
|
error: {
|
|
881
937
|
color: wonderBlocksTokens.color.red
|
|
882
938
|
},
|
|
939
|
+
lightError: {
|
|
940
|
+
color: wonderBlocksTokens.color.fadedRed
|
|
941
|
+
},
|
|
883
942
|
required: {
|
|
884
943
|
color: wonderBlocksTokens.color.red
|
|
944
|
+
},
|
|
945
|
+
lightRequired: {
|
|
946
|
+
color: wonderBlocksTokens.color.fadedRed
|
|
885
947
|
}
|
|
886
948
|
});
|
|
887
949
|
|
|
@@ -960,6 +1022,7 @@ class LabeledTextField extends React__namespace.Component {
|
|
|
960
1022
|
id: uniqueId,
|
|
961
1023
|
testId: testId,
|
|
962
1024
|
style: style,
|
|
1025
|
+
light: light,
|
|
963
1026
|
field: React__namespace.createElement(TextField$1, _extends__default["default"]({
|
|
964
1027
|
id: `${uniqueId}-field`,
|
|
965
1028
|
"aria-describedby": ariaDescribedby ? ariaDescribedby : `${uniqueId}-error`,
|
|
@@ -997,7 +1060,7 @@ var labeledTextField = React__namespace.forwardRef((props, ref) => React__namesp
|
|
|
997
1060
|
forwardedRef: ref
|
|
998
1061
|
})));
|
|
999
1062
|
|
|
1000
|
-
const _excluded = ["onChange", "value", "placeholder", "disabled", "id", "testId", "style", "readOnly", "autoComplete", "name", "className", "autoFocus", "rows", "spellCheck", "wrap", "minLength", "maxLength", "onClick", "onKeyDown", "onKeyUp", "onFocus", "onBlur", "validate", "onValidate", "required", "resizeType", "light"];
|
|
1063
|
+
const _excluded = ["onChange", "value", "placeholder", "disabled", "id", "testId", "style", "readOnly", "autoComplete", "name", "className", "autoFocus", "rows", "spellCheck", "wrap", "minLength", "maxLength", "onClick", "onKeyDown", "onKeyUp", "onFocus", "onBlur", "validate", "onValidate", "required", "resizeType", "light", "rootStyle"];
|
|
1001
1064
|
const defaultErrorMessage = "This field is required.";
|
|
1002
1065
|
const StyledTextArea = wonderBlocksCore.addStyle("textarea");
|
|
1003
1066
|
const TextArea = React__namespace.forwardRef(function TextArea(props, ref) {
|
|
@@ -1028,7 +1091,8 @@ const TextArea = React__namespace.forwardRef(function TextArea(props, ref) {
|
|
|
1028
1091
|
onValidate,
|
|
1029
1092
|
required,
|
|
1030
1093
|
resizeType,
|
|
1031
|
-
light
|
|
1094
|
+
light,
|
|
1095
|
+
rootStyle
|
|
1032
1096
|
} = props,
|
|
1033
1097
|
otherProps = _objectWithoutPropertiesLoose__default["default"](props, _excluded);
|
|
1034
1098
|
const [error, setError] = React__namespace.useState(null);
|
|
@@ -1067,15 +1131,15 @@ const TextArea = React__namespace.forwardRef(function TextArea(props, ref) {
|
|
|
1067
1131
|
return [...baseStyles, ...(light ? lightStyles : defaultStyles)];
|
|
1068
1132
|
};
|
|
1069
1133
|
return React__namespace.createElement(wonderBlocksCore.View, {
|
|
1070
|
-
style: {
|
|
1134
|
+
style: [{
|
|
1071
1135
|
width: "100%"
|
|
1072
|
-
}
|
|
1136
|
+
}, rootStyle]
|
|
1073
1137
|
}, React__namespace.createElement(StyledTextArea, _extends__default["default"]({
|
|
1074
1138
|
id: uniqueId,
|
|
1075
1139
|
"data-testid": testId,
|
|
1076
1140
|
ref: ref,
|
|
1077
1141
|
className: className,
|
|
1078
|
-
style: [
|
|
1142
|
+
style: [getStyles(), style],
|
|
1079
1143
|
value: value,
|
|
1080
1144
|
onChange: handleChange,
|
|
1081
1145
|
placeholder: placeholder,
|
|
@@ -1099,12 +1163,13 @@ const TextArea = React__namespace.forwardRef(function TextArea(props, ref) {
|
|
|
1099
1163
|
"aria-invalid": !!error
|
|
1100
1164
|
})));
|
|
1101
1165
|
});
|
|
1166
|
+
const VERTICAL_SPACING_PX = 10;
|
|
1102
1167
|
const styles = aphrodite.StyleSheet.create({
|
|
1103
1168
|
textarea: {
|
|
1104
1169
|
borderRadius: wonderBlocksTokens.border.radius.medium_4,
|
|
1105
1170
|
boxSizing: "border-box",
|
|
1106
|
-
padding:
|
|
1107
|
-
minHeight:
|
|
1171
|
+
padding: `${VERTICAL_SPACING_PX}px ${wonderBlocksTokens.spacing.medium_16}px`,
|
|
1172
|
+
minHeight: `${VERTICAL_SPACING_PX * 2 + wonderBlocksTokens.font.lineHeight.medium + 2 * wonderBlocksTokens.border.width.hairline}px`
|
|
1108
1173
|
},
|
|
1109
1174
|
default: {
|
|
1110
1175
|
background: wonderBlocksTokens.color.white,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@khanacademy/wonder-blocks-form",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.9.1",
|
|
4
4
|
"design": "v1",
|
|
5
5
|
"description": "Form components for Wonder Blocks.",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -16,12 +16,12 @@
|
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"@babel/runtime": "^7.18.6",
|
|
19
|
-
"@khanacademy/wonder-blocks-clickable": "^4.2.
|
|
20
|
-
"@khanacademy/wonder-blocks-core": "^
|
|
21
|
-
"@khanacademy/wonder-blocks-icon": "^4.1.
|
|
22
|
-
"@khanacademy/wonder-blocks-layout": "^2.
|
|
23
|
-
"@khanacademy/wonder-blocks-tokens": "^
|
|
24
|
-
"@khanacademy/wonder-blocks-typography": "^2.1.
|
|
19
|
+
"@khanacademy/wonder-blocks-clickable": "^4.2.7",
|
|
20
|
+
"@khanacademy/wonder-blocks-core": "^7.0.0",
|
|
21
|
+
"@khanacademy/wonder-blocks-icon": "^4.1.4",
|
|
22
|
+
"@khanacademy/wonder-blocks-layout": "^2.2.0",
|
|
23
|
+
"@khanacademy/wonder-blocks-tokens": "^2.0.0",
|
|
24
|
+
"@khanacademy/wonder-blocks-typography": "^2.1.15"
|
|
25
25
|
},
|
|
26
26
|
"peerDependencies": {
|
|
27
27
|
"aphrodite": "^1.2.5",
|
|
@@ -3,7 +3,6 @@ import {render, screen, fireEvent} from "@testing-library/react";
|
|
|
3
3
|
import {userEvent} from "@testing-library/user-event";
|
|
4
4
|
|
|
5
5
|
import {StyleSheet} from "aphrodite";
|
|
6
|
-
import {color} from "@khanacademy/wonder-blocks-tokens";
|
|
7
6
|
import LabeledTextField from "../labeled-text-field";
|
|
8
7
|
|
|
9
8
|
describe("LabeledTextField", () => {
|
|
@@ -382,28 +381,6 @@ describe("LabeledTextField", () => {
|
|
|
382
381
|
expect(input).toBeInTheDocument();
|
|
383
382
|
});
|
|
384
383
|
|
|
385
|
-
it("light prop is passed to textfield", async () => {
|
|
386
|
-
// Arrange
|
|
387
|
-
|
|
388
|
-
// Act
|
|
389
|
-
render(
|
|
390
|
-
<LabeledTextField
|
|
391
|
-
label="Label"
|
|
392
|
-
value=""
|
|
393
|
-
onChange={() => {}}
|
|
394
|
-
light={true}
|
|
395
|
-
/>,
|
|
396
|
-
);
|
|
397
|
-
|
|
398
|
-
const textField = await screen.findByRole("textbox");
|
|
399
|
-
textField.focus();
|
|
400
|
-
|
|
401
|
-
// Assert
|
|
402
|
-
expect(textField).toHaveStyle({
|
|
403
|
-
boxShadow: `0px 0px 0px 1px ${color.blue}, 0px 0px 0px 2px ${color.white}`,
|
|
404
|
-
});
|
|
405
|
-
});
|
|
406
|
-
|
|
407
384
|
it("style prop is passed to fieldheading", async () => {
|
|
408
385
|
// Arrange
|
|
409
386
|
const styles = StyleSheet.create({
|
|
@@ -42,6 +42,10 @@ type Props = {
|
|
|
42
42
|
* Optional test ID for e2e testing.
|
|
43
43
|
*/
|
|
44
44
|
testId?: string;
|
|
45
|
+
/**
|
|
46
|
+
* Change the field’s sub-components to fit a dark background.
|
|
47
|
+
*/
|
|
48
|
+
light?: boolean;
|
|
45
49
|
};
|
|
46
50
|
|
|
47
51
|
const StyledSpan = addStyle("span");
|
|
@@ -52,10 +56,13 @@ const StyledSpan = addStyle("span");
|
|
|
52
56
|
*/
|
|
53
57
|
export default class FieldHeading extends React.Component<Props> {
|
|
54
58
|
renderLabel(): React.ReactNode {
|
|
55
|
-
const {label, id, required, testId} = this.props;
|
|
59
|
+
const {label, id, required, testId, light} = this.props;
|
|
56
60
|
|
|
57
61
|
const requiredIcon = (
|
|
58
|
-
<StyledSpan
|
|
62
|
+
<StyledSpan
|
|
63
|
+
style={light ? styles.lightRequired : styles.required}
|
|
64
|
+
aria-hidden={true}
|
|
65
|
+
>
|
|
59
66
|
{" "}
|
|
60
67
|
*
|
|
61
68
|
</StyledSpan>
|
|
@@ -64,7 +71,7 @@ export default class FieldHeading extends React.Component<Props> {
|
|
|
64
71
|
return (
|
|
65
72
|
<React.Fragment>
|
|
66
73
|
<LabelMedium
|
|
67
|
-
style={styles.label}
|
|
74
|
+
style={light ? styles.lightLabel : styles.label}
|
|
68
75
|
tag="label"
|
|
69
76
|
htmlFor={id && `${id}-field`}
|
|
70
77
|
testId={testId && `${testId}-label`}
|
|
@@ -78,7 +85,7 @@ export default class FieldHeading extends React.Component<Props> {
|
|
|
78
85
|
}
|
|
79
86
|
|
|
80
87
|
maybeRenderDescription(): React.ReactNode | null | undefined {
|
|
81
|
-
const {description, testId} = this.props;
|
|
88
|
+
const {description, testId, light} = this.props;
|
|
82
89
|
|
|
83
90
|
if (!description) {
|
|
84
91
|
return null;
|
|
@@ -87,7 +94,7 @@ export default class FieldHeading extends React.Component<Props> {
|
|
|
87
94
|
return (
|
|
88
95
|
<React.Fragment>
|
|
89
96
|
<LabelSmall
|
|
90
|
-
style={styles.description}
|
|
97
|
+
style={light ? styles.lightDescription : styles.description}
|
|
91
98
|
testId={testId && `${testId}-description`}
|
|
92
99
|
>
|
|
93
100
|
{description}
|
|
@@ -98,7 +105,7 @@ export default class FieldHeading extends React.Component<Props> {
|
|
|
98
105
|
}
|
|
99
106
|
|
|
100
107
|
maybeRenderError(): React.ReactNode | null | undefined {
|
|
101
|
-
const {error, id, testId} = this.props;
|
|
108
|
+
const {error, id, testId, light} = this.props;
|
|
102
109
|
|
|
103
110
|
if (!error) {
|
|
104
111
|
return null;
|
|
@@ -108,7 +115,7 @@ export default class FieldHeading extends React.Component<Props> {
|
|
|
108
115
|
<React.Fragment>
|
|
109
116
|
<Strut size={spacing.small_12} />
|
|
110
117
|
<LabelSmall
|
|
111
|
-
style={styles.error}
|
|
118
|
+
style={light ? styles.lightError : styles.error}
|
|
112
119
|
role="alert"
|
|
113
120
|
id={id && `${id}-error`}
|
|
114
121
|
testId={testId && `${testId}-error`}
|
|
@@ -138,13 +145,25 @@ const styles = StyleSheet.create({
|
|
|
138
145
|
label: {
|
|
139
146
|
color: color.offBlack,
|
|
140
147
|
},
|
|
148
|
+
lightLabel: {
|
|
149
|
+
color: color.white,
|
|
150
|
+
},
|
|
141
151
|
description: {
|
|
142
152
|
color: color.offBlack64,
|
|
143
153
|
},
|
|
154
|
+
lightDescription: {
|
|
155
|
+
color: color.white64,
|
|
156
|
+
},
|
|
144
157
|
error: {
|
|
145
158
|
color: color.red,
|
|
146
159
|
},
|
|
160
|
+
lightError: {
|
|
161
|
+
color: color.fadedRed,
|
|
162
|
+
},
|
|
147
163
|
required: {
|
|
148
164
|
color: color.red,
|
|
149
165
|
},
|
|
166
|
+
lightRequired: {
|
|
167
|
+
color: color.fadedRed,
|
|
168
|
+
},
|
|
150
169
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import {
|
|
2
|
+
import {StyleSheet} from "aphrodite";
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
5
|
AriaProps,
|
|
@@ -9,7 +9,13 @@ import {
|
|
|
9
9
|
addStyle,
|
|
10
10
|
View,
|
|
11
11
|
} from "@khanacademy/wonder-blocks-core";
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
border,
|
|
14
|
+
color,
|
|
15
|
+
font,
|
|
16
|
+
mix,
|
|
17
|
+
spacing,
|
|
18
|
+
} from "@khanacademy/wonder-blocks-tokens";
|
|
13
19
|
import {styles as typographyStyles} from "@khanacademy/wonder-blocks-typography";
|
|
14
20
|
|
|
15
21
|
type TextAreaProps = AriaProps & {
|
|
@@ -31,9 +37,15 @@ type TextAreaProps = AriaProps & {
|
|
|
31
37
|
*/
|
|
32
38
|
testId?: string;
|
|
33
39
|
/**
|
|
34
|
-
* Custom styles for the
|
|
40
|
+
* Custom styles for the textarea element.
|
|
35
41
|
*/
|
|
36
42
|
style?: StyleType;
|
|
43
|
+
/**
|
|
44
|
+
* Custom styles for the root node of the component.
|
|
45
|
+
* If possible, try to use this prop carefully and use the `style` prop
|
|
46
|
+
* instead.
|
|
47
|
+
*/
|
|
48
|
+
rootStyle?: StyleType;
|
|
37
49
|
/**
|
|
38
50
|
* Provide hints or examples of what to enter.
|
|
39
51
|
*/
|
|
@@ -198,6 +210,7 @@ const TextArea = React.forwardRef<HTMLTextAreaElement, TextAreaProps>(
|
|
|
198
210
|
required,
|
|
199
211
|
resizeType,
|
|
200
212
|
light,
|
|
213
|
+
rootStyle,
|
|
201
214
|
// Should only include aria related props
|
|
202
215
|
...otherProps
|
|
203
216
|
} = props;
|
|
@@ -243,7 +256,7 @@ const TextArea = React.forwardRef<HTMLTextAreaElement, TextAreaProps>(
|
|
|
243
256
|
}
|
|
244
257
|
});
|
|
245
258
|
|
|
246
|
-
const getStyles = ():
|
|
259
|
+
const getStyles = (): StyleType => {
|
|
247
260
|
// Base styles are the styles that apply regardless of light mode
|
|
248
261
|
const baseStyles = [
|
|
249
262
|
styles.textarea,
|
|
@@ -265,13 +278,13 @@ const TextArea = React.forwardRef<HTMLTextAreaElement, TextAreaProps>(
|
|
|
265
278
|
return [...baseStyles, ...(light ? lightStyles : defaultStyles)];
|
|
266
279
|
};
|
|
267
280
|
return (
|
|
268
|
-
<View style={{width: "100%"}}>
|
|
281
|
+
<View style={[{width: "100%"}, rootStyle]}>
|
|
269
282
|
<StyledTextArea
|
|
270
283
|
id={uniqueId}
|
|
271
284
|
data-testid={testId}
|
|
272
285
|
ref={ref}
|
|
273
286
|
className={className}
|
|
274
|
-
style={[
|
|
287
|
+
style={[getStyles(), style]}
|
|
275
288
|
value={value}
|
|
276
289
|
onChange={handleChange}
|
|
277
290
|
placeholder={placeholder}
|
|
@@ -299,12 +312,19 @@ const TextArea = React.forwardRef<HTMLTextAreaElement, TextAreaProps>(
|
|
|
299
312
|
},
|
|
300
313
|
);
|
|
301
314
|
|
|
315
|
+
const VERTICAL_SPACING_PX = 10;
|
|
316
|
+
|
|
302
317
|
const styles = StyleSheet.create({
|
|
303
318
|
textarea: {
|
|
304
319
|
borderRadius: border.radius.medium_4,
|
|
305
320
|
boxSizing: "border-box",
|
|
306
|
-
padding:
|
|
307
|
-
minHeight
|
|
321
|
+
padding: `${VERTICAL_SPACING_PX}px ${spacing.medium_16}px`,
|
|
322
|
+
// This minHeight is equivalent to when the textarea has one row
|
|
323
|
+
minHeight: `${
|
|
324
|
+
VERTICAL_SPACING_PX * 2 +
|
|
325
|
+
font.lineHeight.medium +
|
|
326
|
+
2 * border.width.hairline
|
|
327
|
+
}px`,
|
|
308
328
|
},
|
|
309
329
|
default: {
|
|
310
330
|
background: color.white,
|