@transferwise/components 0.0.0-experimental-5f4dd6a → 0.0.0-experimental-cdd318f
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/build/field/Field.js +63 -32
- package/build/field/Field.js.map +1 -1
- package/build/field/Field.messages.js +14 -0
- package/build/field/Field.messages.js.map +1 -0
- package/build/field/Field.messages.mjs +10 -0
- package/build/field/Field.messages.mjs.map +1 -0
- package/build/field/Field.mjs +65 -34
- package/build/field/Field.mjs.map +1 -1
- package/build/i18n/en.json +1 -0
- package/build/i18n/en.json.js +1 -0
- package/build/i18n/en.json.js.map +1 -1
- package/build/i18n/en.json.mjs +1 -0
- package/build/i18n/en.json.mjs.map +1 -1
- package/build/inputs/TextArea.js +43 -0
- package/build/inputs/TextArea.js.map +1 -1
- package/build/inputs/TextArea.mjs +45 -2
- package/build/inputs/TextArea.mjs.map +1 -1
- package/build/inputs/contexts.js +16 -0
- package/build/inputs/contexts.js.map +1 -1
- package/build/inputs/contexts.mjs +16 -2
- package/build/inputs/contexts.mjs.map +1 -1
- package/build/main.css +21 -3
- package/build/styles/field/Field.css +19 -3
- package/build/styles/main.css +21 -3
- package/build/types/field/Field.d.ts.map +1 -1
- package/build/types/field/Field.messages.d.ts +8 -0
- package/build/types/field/Field.messages.d.ts.map +1 -0
- package/build/types/inputs/TextArea.d.ts.map +1 -1
- package/build/types/inputs/contexts.d.ts +6 -0
- package/build/types/inputs/contexts.d.ts.map +1 -1
- package/build/types/test-utils/index.d.ts +2 -0
- package/build/types/test-utils/index.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/field/Field.css +19 -3
- package/src/field/Field.less +17 -3
- package/src/field/Field.messages.ts +8 -0
- package/src/field/Field.story.tsx +5 -1
- package/src/field/Field.test.tsx +90 -0
- package/src/field/Field.tsx +84 -37
- package/src/i18n/en.json +1 -0
- package/src/inputs/TextArea.story.tsx +93 -0
- package/src/inputs/TextArea.test.story.tsx +142 -0
- package/src/inputs/TextArea.tsx +54 -3
- package/src/inputs/contexts.tsx +18 -1
- package/src/main.css +21 -3
- package/src/textareaWithDisplayFormat/TextareaWithDisplayFormat.story.tsx +1 -0
package/build/inputs/contexts.js
CHANGED
|
@@ -25,6 +25,20 @@ function useInputAttributes({
|
|
|
25
25
|
function useFieldLabelRef() {
|
|
26
26
|
return React.useContext(FieldLabelContext)?.ref;
|
|
27
27
|
}
|
|
28
|
+
const TextareaCharacterCountContext = /*#__PURE__*/React.createContext(undefined);
|
|
29
|
+
const TextareaCharacterCountProvider = TextareaCharacterCountContext.Provider;
|
|
30
|
+
function useTextareaCharacterCount(current, max) {
|
|
31
|
+
const setCharacterCount = React.useContext(TextareaCharacterCountContext);
|
|
32
|
+
React.useEffect(() => {
|
|
33
|
+
if (setCharacterCount && max != null) {
|
|
34
|
+
setCharacterCount({
|
|
35
|
+
current,
|
|
36
|
+
max
|
|
37
|
+
});
|
|
38
|
+
return () => setCharacterCount(null);
|
|
39
|
+
}
|
|
40
|
+
}, [setCharacterCount, current, max]);
|
|
41
|
+
}
|
|
28
42
|
function withInputAttributes(Component, args) {
|
|
29
43
|
function ComponentWithInputAttributes(props) {
|
|
30
44
|
return /*#__PURE__*/jsxRuntime.jsx(Component, {
|
|
@@ -40,7 +54,9 @@ exports.FieldLabelContextProvider = FieldLabelContextProvider;
|
|
|
40
54
|
exports.InputDescribedByProvider = InputDescribedByProvider;
|
|
41
55
|
exports.InputIdContextProvider = InputIdContextProvider;
|
|
42
56
|
exports.InputInvalidProvider = InputInvalidProvider;
|
|
57
|
+
exports.TextareaCharacterCountProvider = TextareaCharacterCountProvider;
|
|
43
58
|
exports.useFieldLabelRef = useFieldLabelRef;
|
|
44
59
|
exports.useInputAttributes = useInputAttributes;
|
|
60
|
+
exports.useTextareaCharacterCount = useTextareaCharacterCount;
|
|
45
61
|
exports.withInputAttributes = withInputAttributes;
|
|
46
62
|
//# sourceMappingURL=contexts.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contexts.js","sources":["../../src/inputs/contexts.tsx"],"sourcesContent":["import { createContext, useContext } from 'react';\n\ntype FieldLabelContextType = {\n id?: string;\n ref?: React.RefObject<HTMLLabelElement>;\n};\n\nconst FieldLabelContext = createContext<FieldLabelContextType | undefined>(undefined);\nexport const FieldLabelContextProvider = FieldLabelContext.Provider;\n\nconst InputIdContext = createContext<string | undefined>(undefined);\nexport const InputIdContextProvider = InputIdContext.Provider;\n\nconst InputDescribedByContext = createContext<string | undefined>(undefined);\nexport const InputDescribedByProvider = InputDescribedByContext.Provider;\n\nconst InputInvalidContext = createContext<boolean | undefined>(undefined);\nexport const InputInvalidProvider = InputInvalidContext.Provider;\n\ninterface UseInputAttributesArgs {\n /** Set this to `true` if the underlying element is not directly [labelable as per the HTML specification](https://html.spec.whatwg.org/multipage/forms.html#category-label). */\n nonLabelable?: boolean;\n}\n\nexport function useInputAttributes({ nonLabelable }: UseInputAttributesArgs = {}) {\n const labelId = useContext(FieldLabelContext)?.id;\n return {\n id: useContext(InputIdContext),\n 'aria-labelledby': nonLabelable ? labelId : undefined,\n 'aria-describedby': useContext(InputDescribedByContext),\n 'aria-invalid': useContext(InputInvalidContext),\n } satisfies React.HTMLAttributes<HTMLElement>;\n}\n\nexport function useFieldLabelRef() {\n return useContext(FieldLabelContext)?.ref;\n}\n\nexport interface WithInputAttributesProps {\n inputAttributes: ReturnType<typeof useInputAttributes>;\n}\n\nexport function withInputAttributes<T extends Partial<WithInputAttributesProps>>(\n Component: React.ComponentType<T>,\n args?: UseInputAttributesArgs,\n) {\n function ComponentWithInputAttributes(props: Omit<T, keyof WithInputAttributesProps>) {\n return <Component inputAttributes={useInputAttributes(args)} {...(props as T)} />;\n }\n\n ComponentWithInputAttributes.displayName = `withInputAttributes(${Component.displayName || Component.name || 'Component'})`;\n\n return ComponentWithInputAttributes;\n}\n"],"names":["FieldLabelContext","createContext","undefined","FieldLabelContextProvider","Provider","InputIdContext","InputIdContextProvider","InputDescribedByContext","InputDescribedByProvider","InputInvalidContext","InputInvalidProvider","useInputAttributes","nonLabelable","labelId","useContext","id","useFieldLabelRef","ref","withInputAttributes","Component","args","ComponentWithInputAttributes","props","_jsx","inputAttributes","displayName","name"],"mappings":";;;;;AAOA,MAAMA,iBAAiB,gBAAGC,mBAAa,CAAoCC,SAAS,CAAC;AAC9E,MAAMC,yBAAyB,GAAGH,iBAAiB,CAACI;AAE3D,MAAMC,cAAc,gBAAGJ,mBAAa,CAAqBC,SAAS,CAAC;AAC5D,MAAMI,sBAAsB,GAAGD,cAAc,CAACD;AAErD,MAAMG,uBAAuB,gBAAGN,mBAAa,CAAqBC,SAAS,CAAC;AACrE,MAAMM,wBAAwB,GAAGD,uBAAuB,CAACH;AAEhE,MAAMK,mBAAmB,gBAAGR,mBAAa,CAAsBC,SAAS,CAAC;AAClE,MAAMQ,oBAAoB,GAAGD,mBAAmB,CAACL;SAOxCO,kBAAkBA,CAAC;AAAEC,EAAAA;IAAyC,EAAE,EAAA;AAC9E,EAAA,MAAMC,OAAO,GAAGC,gBAAU,CAACd,iBAAiB,CAAC,EAAEe,EAAE;EACjD,OAAO;AACLA,IAAAA,EAAE,EAAED,gBAAU,CAACT,cAAc,CAAC;AAC9B,IAAA,iBAAiB,EAAEO,YAAY,GAAGC,OAAO,GAAGX,SAAS;AACrD,IAAA,kBAAkB,EAAEY,gBAAU,CAACP,uBAAuB,CAAC;IACvD,cAAc,EAAEO,gBAAU,CAACL,mBAAmB;GACH;AAC/C;SAEgBO,gBAAgBA,GAAA;AAC9B,EAAA,OAAOF,gBAAU,CAACd,iBAAiB,CAAC,EAAEiB,GAAG;AAC3C;AAMM,
|
|
1
|
+
{"version":3,"file":"contexts.js","sources":["../../src/inputs/contexts.tsx"],"sourcesContent":["import { createContext, useContext, useEffect } from 'react';\n\ntype FieldLabelContextType = {\n id?: string;\n ref?: React.RefObject<HTMLLabelElement>;\n};\n\nconst FieldLabelContext = createContext<FieldLabelContextType | undefined>(undefined);\nexport const FieldLabelContextProvider = FieldLabelContext.Provider;\n\nconst InputIdContext = createContext<string | undefined>(undefined);\nexport const InputIdContextProvider = InputIdContext.Provider;\n\nconst InputDescribedByContext = createContext<string | undefined>(undefined);\nexport const InputDescribedByProvider = InputDescribedByContext.Provider;\n\nconst InputInvalidContext = createContext<boolean | undefined>(undefined);\nexport const InputInvalidProvider = InputInvalidContext.Provider;\n\ninterface UseInputAttributesArgs {\n /** Set this to `true` if the underlying element is not directly [labelable as per the HTML specification](https://html.spec.whatwg.org/multipage/forms.html#category-label). */\n nonLabelable?: boolean;\n}\n\nexport function useInputAttributes({ nonLabelable }: UseInputAttributesArgs = {}) {\n const labelId = useContext(FieldLabelContext)?.id;\n return {\n id: useContext(InputIdContext),\n 'aria-labelledby': nonLabelable ? labelId : undefined,\n 'aria-describedby': useContext(InputDescribedByContext),\n 'aria-invalid': useContext(InputInvalidContext),\n } satisfies React.HTMLAttributes<HTMLElement>;\n}\n\nexport function useFieldLabelRef() {\n return useContext(FieldLabelContext)?.ref;\n}\n\nexport type TextareaCharacterCountState = { current: number; max: number } | null;\n\nconst TextareaCharacterCountContext = createContext<\n ((state: TextareaCharacterCountState) => void) | undefined\n>(undefined);\nexport const TextareaCharacterCountProvider = TextareaCharacterCountContext.Provider;\n\nexport function useTextareaCharacterCount(current: number, max: number | undefined) {\n const setCharacterCount = useContext(TextareaCharacterCountContext);\n useEffect(() => {\n if (setCharacterCount && max != null) {\n setCharacterCount({ current, max });\n return () => setCharacterCount(null);\n }\n }, [setCharacterCount, current, max]);\n}\n\nexport interface WithInputAttributesProps {\n inputAttributes: ReturnType<typeof useInputAttributes>;\n}\n\nexport function withInputAttributes<T extends Partial<WithInputAttributesProps>>(\n Component: React.ComponentType<T>,\n args?: UseInputAttributesArgs,\n) {\n function ComponentWithInputAttributes(props: Omit<T, keyof WithInputAttributesProps>) {\n return <Component inputAttributes={useInputAttributes(args)} {...(props as T)} />;\n }\n\n ComponentWithInputAttributes.displayName = `withInputAttributes(${Component.displayName || Component.name || 'Component'})`;\n\n return ComponentWithInputAttributes;\n}\n"],"names":["FieldLabelContext","createContext","undefined","FieldLabelContextProvider","Provider","InputIdContext","InputIdContextProvider","InputDescribedByContext","InputDescribedByProvider","InputInvalidContext","InputInvalidProvider","useInputAttributes","nonLabelable","labelId","useContext","id","useFieldLabelRef","ref","TextareaCharacterCountContext","TextareaCharacterCountProvider","useTextareaCharacterCount","current","max","setCharacterCount","useEffect","withInputAttributes","Component","args","ComponentWithInputAttributes","props","_jsx","inputAttributes","displayName","name"],"mappings":";;;;;AAOA,MAAMA,iBAAiB,gBAAGC,mBAAa,CAAoCC,SAAS,CAAC;AAC9E,MAAMC,yBAAyB,GAAGH,iBAAiB,CAACI;AAE3D,MAAMC,cAAc,gBAAGJ,mBAAa,CAAqBC,SAAS,CAAC;AAC5D,MAAMI,sBAAsB,GAAGD,cAAc,CAACD;AAErD,MAAMG,uBAAuB,gBAAGN,mBAAa,CAAqBC,SAAS,CAAC;AACrE,MAAMM,wBAAwB,GAAGD,uBAAuB,CAACH;AAEhE,MAAMK,mBAAmB,gBAAGR,mBAAa,CAAsBC,SAAS,CAAC;AAClE,MAAMQ,oBAAoB,GAAGD,mBAAmB,CAACL;SAOxCO,kBAAkBA,CAAC;AAAEC,EAAAA;IAAyC,EAAE,EAAA;AAC9E,EAAA,MAAMC,OAAO,GAAGC,gBAAU,CAACd,iBAAiB,CAAC,EAAEe,EAAE;EACjD,OAAO;AACLA,IAAAA,EAAE,EAAED,gBAAU,CAACT,cAAc,CAAC;AAC9B,IAAA,iBAAiB,EAAEO,YAAY,GAAGC,OAAO,GAAGX,SAAS;AACrD,IAAA,kBAAkB,EAAEY,gBAAU,CAACP,uBAAuB,CAAC;IACvD,cAAc,EAAEO,gBAAU,CAACL,mBAAmB;GACH;AAC/C;SAEgBO,gBAAgBA,GAAA;AAC9B,EAAA,OAAOF,gBAAU,CAACd,iBAAiB,CAAC,EAAEiB,GAAG;AAC3C;AAIA,MAAMC,6BAA6B,gBAAGjB,mBAAa,CAEjDC,SAAS,CAAC;AACL,MAAMiB,8BAA8B,GAAGD,6BAA6B,CAACd;AAEtE,SAAUgB,yBAAyBA,CAACC,OAAe,EAAEC,GAAuB,EAAA;AAChF,EAAA,MAAMC,iBAAiB,GAAGT,gBAAU,CAACI,6BAA6B,CAAC;AACnEM,EAAAA,eAAS,CAAC,MAAK;AACb,IAAA,IAAID,iBAAiB,IAAID,GAAG,IAAI,IAAI,EAAE;AACpCC,MAAAA,iBAAiB,CAAC;QAAEF,OAAO;AAAEC,QAAAA;AAAG,OAAE,CAAC;AACnC,MAAA,OAAO,MAAMC,iBAAiB,CAAC,IAAI,CAAC;AACtC,IAAA;EACF,CAAC,EAAE,CAACA,iBAAiB,EAAEF,OAAO,EAAEC,GAAG,CAAC,CAAC;AACvC;AAMM,SAAUG,mBAAmBA,CACjCC,SAAiC,EACjCC,IAA6B,EAAA;EAE7B,SAASC,4BAA4BA,CAACC,KAA8C,EAAA;IAClF,oBAAOC,cAAA,CAACJ,SAAS,EAAA;AAACK,MAAAA,eAAe,EAAEpB,kBAAkB,CAACgB,IAAI,CAAE;MAAA,GAAME;AAAW,MAAI;AACnF,EAAA;AAEAD,EAAAA,4BAA4B,CAACI,WAAW,GAAG,CAAA,oBAAA,EAAuBN,SAAS,CAACM,WAAW,IAAIN,SAAS,CAACO,IAAI,IAAI,WAAW,CAAA,CAAA,CAAG;AAE3H,EAAA,OAAOL,4BAA4B;AACrC;;;;;;;;;;;;"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createContext, useContext } from 'react';
|
|
1
|
+
import { createContext, useContext, useEffect } from 'react';
|
|
2
2
|
import { jsx } from 'react/jsx-runtime';
|
|
3
3
|
|
|
4
4
|
const FieldLabelContext = /*#__PURE__*/createContext(undefined);
|
|
@@ -23,6 +23,20 @@ function useInputAttributes({
|
|
|
23
23
|
function useFieldLabelRef() {
|
|
24
24
|
return useContext(FieldLabelContext)?.ref;
|
|
25
25
|
}
|
|
26
|
+
const TextareaCharacterCountContext = /*#__PURE__*/createContext(undefined);
|
|
27
|
+
const TextareaCharacterCountProvider = TextareaCharacterCountContext.Provider;
|
|
28
|
+
function useTextareaCharacterCount(current, max) {
|
|
29
|
+
const setCharacterCount = useContext(TextareaCharacterCountContext);
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
if (setCharacterCount && max != null) {
|
|
32
|
+
setCharacterCount({
|
|
33
|
+
current,
|
|
34
|
+
max
|
|
35
|
+
});
|
|
36
|
+
return () => setCharacterCount(null);
|
|
37
|
+
}
|
|
38
|
+
}, [setCharacterCount, current, max]);
|
|
39
|
+
}
|
|
26
40
|
function withInputAttributes(Component, args) {
|
|
27
41
|
function ComponentWithInputAttributes(props) {
|
|
28
42
|
return /*#__PURE__*/jsx(Component, {
|
|
@@ -34,5 +48,5 @@ function withInputAttributes(Component, args) {
|
|
|
34
48
|
return ComponentWithInputAttributes;
|
|
35
49
|
}
|
|
36
50
|
|
|
37
|
-
export { FieldLabelContextProvider, InputDescribedByProvider, InputIdContextProvider, InputInvalidProvider, useFieldLabelRef, useInputAttributes, withInputAttributes };
|
|
51
|
+
export { FieldLabelContextProvider, InputDescribedByProvider, InputIdContextProvider, InputInvalidProvider, TextareaCharacterCountProvider, useFieldLabelRef, useInputAttributes, useTextareaCharacterCount, withInputAttributes };
|
|
38
52
|
//# sourceMappingURL=contexts.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contexts.mjs","sources":["../../src/inputs/contexts.tsx"],"sourcesContent":["import { createContext, useContext } from 'react';\n\ntype FieldLabelContextType = {\n id?: string;\n ref?: React.RefObject<HTMLLabelElement>;\n};\n\nconst FieldLabelContext = createContext<FieldLabelContextType | undefined>(undefined);\nexport const FieldLabelContextProvider = FieldLabelContext.Provider;\n\nconst InputIdContext = createContext<string | undefined>(undefined);\nexport const InputIdContextProvider = InputIdContext.Provider;\n\nconst InputDescribedByContext = createContext<string | undefined>(undefined);\nexport const InputDescribedByProvider = InputDescribedByContext.Provider;\n\nconst InputInvalidContext = createContext<boolean | undefined>(undefined);\nexport const InputInvalidProvider = InputInvalidContext.Provider;\n\ninterface UseInputAttributesArgs {\n /** Set this to `true` if the underlying element is not directly [labelable as per the HTML specification](https://html.spec.whatwg.org/multipage/forms.html#category-label). */\n nonLabelable?: boolean;\n}\n\nexport function useInputAttributes({ nonLabelable }: UseInputAttributesArgs = {}) {\n const labelId = useContext(FieldLabelContext)?.id;\n return {\n id: useContext(InputIdContext),\n 'aria-labelledby': nonLabelable ? labelId : undefined,\n 'aria-describedby': useContext(InputDescribedByContext),\n 'aria-invalid': useContext(InputInvalidContext),\n } satisfies React.HTMLAttributes<HTMLElement>;\n}\n\nexport function useFieldLabelRef() {\n return useContext(FieldLabelContext)?.ref;\n}\n\nexport interface WithInputAttributesProps {\n inputAttributes: ReturnType<typeof useInputAttributes>;\n}\n\nexport function withInputAttributes<T extends Partial<WithInputAttributesProps>>(\n Component: React.ComponentType<T>,\n args?: UseInputAttributesArgs,\n) {\n function ComponentWithInputAttributes(props: Omit<T, keyof WithInputAttributesProps>) {\n return <Component inputAttributes={useInputAttributes(args)} {...(props as T)} />;\n }\n\n ComponentWithInputAttributes.displayName = `withInputAttributes(${Component.displayName || Component.name || 'Component'})`;\n\n return ComponentWithInputAttributes;\n}\n"],"names":["FieldLabelContext","createContext","undefined","FieldLabelContextProvider","Provider","InputIdContext","InputIdContextProvider","InputDescribedByContext","InputDescribedByProvider","InputInvalidContext","InputInvalidProvider","useInputAttributes","nonLabelable","labelId","useContext","id","useFieldLabelRef","ref","withInputAttributes","Component","args","ComponentWithInputAttributes","props","_jsx","inputAttributes","displayName","name"],"mappings":";;;AAOA,MAAMA,iBAAiB,gBAAGC,aAAa,CAAoCC,SAAS,CAAC;AAC9E,MAAMC,yBAAyB,GAAGH,iBAAiB,CAACI;AAE3D,MAAMC,cAAc,gBAAGJ,aAAa,CAAqBC,SAAS,CAAC;AAC5D,MAAMI,sBAAsB,GAAGD,cAAc,CAACD;AAErD,MAAMG,uBAAuB,gBAAGN,aAAa,CAAqBC,SAAS,CAAC;AACrE,MAAMM,wBAAwB,GAAGD,uBAAuB,CAACH;AAEhE,MAAMK,mBAAmB,gBAAGR,aAAa,CAAsBC,SAAS,CAAC;AAClE,MAAMQ,oBAAoB,GAAGD,mBAAmB,CAACL;SAOxCO,kBAAkBA,CAAC;AAAEC,EAAAA;IAAyC,EAAE,EAAA;AAC9E,EAAA,MAAMC,OAAO,GAAGC,UAAU,CAACd,iBAAiB,CAAC,EAAEe,EAAE;EACjD,OAAO;AACLA,IAAAA,EAAE,EAAED,UAAU,CAACT,cAAc,CAAC;AAC9B,IAAA,iBAAiB,EAAEO,YAAY,GAAGC,OAAO,GAAGX,SAAS;AACrD,IAAA,kBAAkB,EAAEY,UAAU,CAACP,uBAAuB,CAAC;IACvD,cAAc,EAAEO,UAAU,CAACL,mBAAmB;GACH;AAC/C;SAEgBO,gBAAgBA,GAAA;AAC9B,EAAA,OAAOF,UAAU,CAACd,iBAAiB,CAAC,EAAEiB,GAAG;AAC3C;AAMM,
|
|
1
|
+
{"version":3,"file":"contexts.mjs","sources":["../../src/inputs/contexts.tsx"],"sourcesContent":["import { createContext, useContext, useEffect } from 'react';\n\ntype FieldLabelContextType = {\n id?: string;\n ref?: React.RefObject<HTMLLabelElement>;\n};\n\nconst FieldLabelContext = createContext<FieldLabelContextType | undefined>(undefined);\nexport const FieldLabelContextProvider = FieldLabelContext.Provider;\n\nconst InputIdContext = createContext<string | undefined>(undefined);\nexport const InputIdContextProvider = InputIdContext.Provider;\n\nconst InputDescribedByContext = createContext<string | undefined>(undefined);\nexport const InputDescribedByProvider = InputDescribedByContext.Provider;\n\nconst InputInvalidContext = createContext<boolean | undefined>(undefined);\nexport const InputInvalidProvider = InputInvalidContext.Provider;\n\ninterface UseInputAttributesArgs {\n /** Set this to `true` if the underlying element is not directly [labelable as per the HTML specification](https://html.spec.whatwg.org/multipage/forms.html#category-label). */\n nonLabelable?: boolean;\n}\n\nexport function useInputAttributes({ nonLabelable }: UseInputAttributesArgs = {}) {\n const labelId = useContext(FieldLabelContext)?.id;\n return {\n id: useContext(InputIdContext),\n 'aria-labelledby': nonLabelable ? labelId : undefined,\n 'aria-describedby': useContext(InputDescribedByContext),\n 'aria-invalid': useContext(InputInvalidContext),\n } satisfies React.HTMLAttributes<HTMLElement>;\n}\n\nexport function useFieldLabelRef() {\n return useContext(FieldLabelContext)?.ref;\n}\n\nexport type TextareaCharacterCountState = { current: number; max: number } | null;\n\nconst TextareaCharacterCountContext = createContext<\n ((state: TextareaCharacterCountState) => void) | undefined\n>(undefined);\nexport const TextareaCharacterCountProvider = TextareaCharacterCountContext.Provider;\n\nexport function useTextareaCharacterCount(current: number, max: number | undefined) {\n const setCharacterCount = useContext(TextareaCharacterCountContext);\n useEffect(() => {\n if (setCharacterCount && max != null) {\n setCharacterCount({ current, max });\n return () => setCharacterCount(null);\n }\n }, [setCharacterCount, current, max]);\n}\n\nexport interface WithInputAttributesProps {\n inputAttributes: ReturnType<typeof useInputAttributes>;\n}\n\nexport function withInputAttributes<T extends Partial<WithInputAttributesProps>>(\n Component: React.ComponentType<T>,\n args?: UseInputAttributesArgs,\n) {\n function ComponentWithInputAttributes(props: Omit<T, keyof WithInputAttributesProps>) {\n return <Component inputAttributes={useInputAttributes(args)} {...(props as T)} />;\n }\n\n ComponentWithInputAttributes.displayName = `withInputAttributes(${Component.displayName || Component.name || 'Component'})`;\n\n return ComponentWithInputAttributes;\n}\n"],"names":["FieldLabelContext","createContext","undefined","FieldLabelContextProvider","Provider","InputIdContext","InputIdContextProvider","InputDescribedByContext","InputDescribedByProvider","InputInvalidContext","InputInvalidProvider","useInputAttributes","nonLabelable","labelId","useContext","id","useFieldLabelRef","ref","TextareaCharacterCountContext","TextareaCharacterCountProvider","useTextareaCharacterCount","current","max","setCharacterCount","useEffect","withInputAttributes","Component","args","ComponentWithInputAttributes","props","_jsx","inputAttributes","displayName","name"],"mappings":";;;AAOA,MAAMA,iBAAiB,gBAAGC,aAAa,CAAoCC,SAAS,CAAC;AAC9E,MAAMC,yBAAyB,GAAGH,iBAAiB,CAACI;AAE3D,MAAMC,cAAc,gBAAGJ,aAAa,CAAqBC,SAAS,CAAC;AAC5D,MAAMI,sBAAsB,GAAGD,cAAc,CAACD;AAErD,MAAMG,uBAAuB,gBAAGN,aAAa,CAAqBC,SAAS,CAAC;AACrE,MAAMM,wBAAwB,GAAGD,uBAAuB,CAACH;AAEhE,MAAMK,mBAAmB,gBAAGR,aAAa,CAAsBC,SAAS,CAAC;AAClE,MAAMQ,oBAAoB,GAAGD,mBAAmB,CAACL;SAOxCO,kBAAkBA,CAAC;AAAEC,EAAAA;IAAyC,EAAE,EAAA;AAC9E,EAAA,MAAMC,OAAO,GAAGC,UAAU,CAACd,iBAAiB,CAAC,EAAEe,EAAE;EACjD,OAAO;AACLA,IAAAA,EAAE,EAAED,UAAU,CAACT,cAAc,CAAC;AAC9B,IAAA,iBAAiB,EAAEO,YAAY,GAAGC,OAAO,GAAGX,SAAS;AACrD,IAAA,kBAAkB,EAAEY,UAAU,CAACP,uBAAuB,CAAC;IACvD,cAAc,EAAEO,UAAU,CAACL,mBAAmB;GACH;AAC/C;SAEgBO,gBAAgBA,GAAA;AAC9B,EAAA,OAAOF,UAAU,CAACd,iBAAiB,CAAC,EAAEiB,GAAG;AAC3C;AAIA,MAAMC,6BAA6B,gBAAGjB,aAAa,CAEjDC,SAAS,CAAC;AACL,MAAMiB,8BAA8B,GAAGD,6BAA6B,CAACd;AAEtE,SAAUgB,yBAAyBA,CAACC,OAAe,EAAEC,GAAuB,EAAA;AAChF,EAAA,MAAMC,iBAAiB,GAAGT,UAAU,CAACI,6BAA6B,CAAC;AACnEM,EAAAA,SAAS,CAAC,MAAK;AACb,IAAA,IAAID,iBAAiB,IAAID,GAAG,IAAI,IAAI,EAAE;AACpCC,MAAAA,iBAAiB,CAAC;QAAEF,OAAO;AAAEC,QAAAA;AAAG,OAAE,CAAC;AACnC,MAAA,OAAO,MAAMC,iBAAiB,CAAC,IAAI,CAAC;AACtC,IAAA;EACF,CAAC,EAAE,CAACA,iBAAiB,EAAEF,OAAO,EAAEC,GAAG,CAAC,CAAC;AACvC;AAMM,SAAUG,mBAAmBA,CACjCC,SAAiC,EACjCC,IAA6B,EAAA;EAE7B,SAASC,4BAA4BA,CAACC,KAA8C,EAAA;IAClF,oBAAOC,GAAA,CAACJ,SAAS,EAAA;AAACK,MAAAA,eAAe,EAAEpB,kBAAkB,CAACgB,IAAI,CAAE;MAAA,GAAME;AAAW,MAAI;AACnF,EAAA;AAEAD,EAAAA,4BAA4B,CAACI,WAAW,GAAG,CAAA,oBAAA,EAAuBN,SAAS,CAACM,WAAW,IAAIN,SAAS,CAACO,IAAI,IAAI,WAAW,CAAA,CAAA,CAAG;AAE3H,EAAA,OAAOL,4BAA4B;AACrC;;;;"}
|
package/build/main.css
CHANGED
|
@@ -29935,18 +29935,36 @@ html:not([dir="rtl"]) .np-flow-navigation--sm .np-flow-navigation__stepper {
|
|
|
29935
29935
|
stroke-dasharray: var(--wds-list-item-spotlight-strokeDashSize) var(--wds-list-item-spotlight-strokeDashSize);
|
|
29936
29936
|
}
|
|
29937
29937
|
|
|
29938
|
-
.np-field-control
|
|
29939
|
-
.np-field__prompt {
|
|
29938
|
+
.np-field-control {
|
|
29940
29939
|
margin-top: 4px;
|
|
29941
29940
|
margin-top: var(--size-4);
|
|
29942
29941
|
}
|
|
29943
29942
|
|
|
29943
|
+
.np-field-validation {
|
|
29944
|
+
display: flex;
|
|
29945
|
+
align-items: flex-start;
|
|
29946
|
+
margin-top: 4px;
|
|
29947
|
+
margin-top: var(--size-4);
|
|
29948
|
+
gap: 8px;
|
|
29949
|
+
gap: var(--size-8);
|
|
29950
|
+
}
|
|
29951
|
+
|
|
29952
|
+
.np-field-textarea-char-counter {
|
|
29953
|
+
min-width: 55px;
|
|
29954
|
+
text-align: right;
|
|
29955
|
+
margin-left: auto;
|
|
29956
|
+
padding: 4px 0;
|
|
29957
|
+
padding: var(--size-4) 0;
|
|
29958
|
+
color: #768e9c;
|
|
29959
|
+
color: var(--color-content-tertiary);
|
|
29960
|
+
}
|
|
29961
|
+
|
|
29944
29962
|
.np-field .form-group--typeahead[class],
|
|
29945
29963
|
.np-field .np-checkbox-label[class] {
|
|
29946
29964
|
margin-bottom: 0;
|
|
29947
29965
|
}
|
|
29948
29966
|
|
|
29949
|
-
.np-field:has(.wds-radio-group) .np-
|
|
29967
|
+
.np-field:has(.wds-radio-group) .np-field-validation {
|
|
29950
29968
|
margin-top: 12px;
|
|
29951
29969
|
margin-top: var(--size-12);
|
|
29952
29970
|
}
|
|
@@ -1,13 +1,29 @@
|
|
|
1
|
-
.np-field-control
|
|
2
|
-
.np-field__prompt {
|
|
1
|
+
.np-field-control {
|
|
3
2
|
margin-top: 4px;
|
|
4
3
|
margin-top: var(--size-4);
|
|
5
4
|
}
|
|
5
|
+
.np-field-validation {
|
|
6
|
+
display: flex;
|
|
7
|
+
align-items: flex-start;
|
|
8
|
+
margin-top: 4px;
|
|
9
|
+
margin-top: var(--size-4);
|
|
10
|
+
gap: 8px;
|
|
11
|
+
gap: var(--size-8);
|
|
12
|
+
}
|
|
13
|
+
.np-field-textarea-char-counter {
|
|
14
|
+
min-width: 55px;
|
|
15
|
+
text-align: right;
|
|
16
|
+
margin-left: auto;
|
|
17
|
+
padding: 4px 0;
|
|
18
|
+
padding: var(--size-4) 0;
|
|
19
|
+
color: #768e9c;
|
|
20
|
+
color: var(--color-content-tertiary);
|
|
21
|
+
}
|
|
6
22
|
.np-field .form-group--typeahead[class],
|
|
7
23
|
.np-field .np-checkbox-label[class] {
|
|
8
24
|
margin-bottom: 0;
|
|
9
25
|
}
|
|
10
|
-
.np-field:has(.wds-radio-group) .np-
|
|
26
|
+
.np-field:has(.wds-radio-group) .np-field-validation {
|
|
11
27
|
margin-top: 12px;
|
|
12
28
|
margin-top: var(--size-12);
|
|
13
29
|
}
|
package/build/styles/main.css
CHANGED
|
@@ -29935,18 +29935,36 @@ html:not([dir="rtl"]) .np-flow-navigation--sm .np-flow-navigation__stepper {
|
|
|
29935
29935
|
stroke-dasharray: var(--wds-list-item-spotlight-strokeDashSize) var(--wds-list-item-spotlight-strokeDashSize);
|
|
29936
29936
|
}
|
|
29937
29937
|
|
|
29938
|
-
.np-field-control
|
|
29939
|
-
.np-field__prompt {
|
|
29938
|
+
.np-field-control {
|
|
29940
29939
|
margin-top: 4px;
|
|
29941
29940
|
margin-top: var(--size-4);
|
|
29942
29941
|
}
|
|
29943
29942
|
|
|
29943
|
+
.np-field-validation {
|
|
29944
|
+
display: flex;
|
|
29945
|
+
align-items: flex-start;
|
|
29946
|
+
margin-top: 4px;
|
|
29947
|
+
margin-top: var(--size-4);
|
|
29948
|
+
gap: 8px;
|
|
29949
|
+
gap: var(--size-8);
|
|
29950
|
+
}
|
|
29951
|
+
|
|
29952
|
+
.np-field-textarea-char-counter {
|
|
29953
|
+
min-width: 55px;
|
|
29954
|
+
text-align: right;
|
|
29955
|
+
margin-left: auto;
|
|
29956
|
+
padding: 4px 0;
|
|
29957
|
+
padding: var(--size-4) 0;
|
|
29958
|
+
color: #768e9c;
|
|
29959
|
+
color: var(--color-content-tertiary);
|
|
29960
|
+
}
|
|
29961
|
+
|
|
29944
29962
|
.np-field .form-group--typeahead[class],
|
|
29945
29963
|
.np-field .np-checkbox-label[class] {
|
|
29946
29964
|
margin-bottom: 0;
|
|
29947
29965
|
}
|
|
29948
29966
|
|
|
29949
|
-
.np-field:has(.wds-radio-group) .np-
|
|
29967
|
+
.np-field:has(.wds-radio-group) .np-field-validation {
|
|
29950
29968
|
margin-top: 12px;
|
|
29951
29969
|
margin-top: var(--size-12);
|
|
29952
29970
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Field.d.ts","sourceRoot":"","sources":["../../../src/field/Field.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Field.d.ts","sourceRoot":"","sources":["../../../src/field/Field.tsx"],"names":[],"mappings":"AAOA,OAAO,EAAgB,KAAK,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAWjE,MAAM,MAAM,UAAU,GAAG;IACvB,6IAA6I;IAC7I,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,sGAAsG;IACtG,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,oBAAoB;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,kDAAkD;IAClD,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACvB,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B;;;SAGK;IACL,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B,8EAA8E;IAC9E,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,iCAAiC;IACjC,SAAS,CAAC,EAAE,iBAAiB,CAAC,WAAW,CAAC,CAAC;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B,CAAC;AAEF,eAAO,MAAM,KAAK,GAAI,wJAanB,UAAU,gCAsHZ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Field.messages.d.ts","sourceRoot":"","sources":["../../../src/field/Field.messages.ts"],"names":[],"mappings":";;;;;;AAEA,wBAKG"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TextArea.d.ts","sourceRoot":"","sources":["../../../src/inputs/TextArea.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAIjC,MAAM,WAAW,aAAc,SAAQ,KAAK,CAC1C,KAAK,CAAC,qBAAqB,CAAC,UAAU,CAAC,EACvC;IACE,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,CACF;CAAG;AAEJ,eAAO,MAAM,QAAQ,
|
|
1
|
+
{"version":3,"file":"TextArea.d.ts","sourceRoot":"","sources":["../../../src/inputs/TextArea.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAIjC,MAAM,WAAW,aAAc,SAAQ,KAAK,CAC1C,KAAK,CAAC,qBAAqB,CAAC,UAAU,CAAC,EACvC;IACE,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,CACF;CAAG;AAEJ,eAAO,MAAM,QAAQ,mIAmCnB,CAAC"}
|
|
@@ -17,6 +17,12 @@ export declare function useInputAttributes({ nonLabelable }?: UseInputAttributes
|
|
|
17
17
|
'aria-invalid': boolean | undefined;
|
|
18
18
|
};
|
|
19
19
|
export declare function useFieldLabelRef(): import("react").RefObject<HTMLLabelElement> | undefined;
|
|
20
|
+
export type TextareaCharacterCountState = {
|
|
21
|
+
current: number;
|
|
22
|
+
max: number;
|
|
23
|
+
} | null;
|
|
24
|
+
export declare const TextareaCharacterCountProvider: import("react").Provider<((state: TextareaCharacterCountState) => void) | undefined>;
|
|
25
|
+
export declare function useTextareaCharacterCount(current: number, max: number | undefined): void;
|
|
20
26
|
export interface WithInputAttributesProps {
|
|
21
27
|
inputAttributes: ReturnType<typeof useInputAttributes>;
|
|
22
28
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contexts.d.ts","sourceRoot":"","sources":["../../../src/inputs/contexts.tsx"],"names":[],"mappings":"AAEA,KAAK,qBAAqB,GAAG;IAC3B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;CACzC,CAAC;AAGF,eAAO,MAAM,yBAAyB,6DAA6B,CAAC;AAGpE,eAAO,MAAM,sBAAsB,8CAA0B,CAAC;AAG9D,eAAO,MAAM,wBAAwB,8CAAmC,CAAC;AAGzE,eAAO,MAAM,oBAAoB,+CAA+B,CAAC;AAEjE,UAAU,sBAAsB;IAC9B,gLAAgL;IAChL,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,wBAAgB,kBAAkB,CAAC,EAAE,YAAY,EAAE,GAAE,sBAA2B;;;;;EAQ/E;AAED,wBAAgB,gBAAgB,4DAE/B;AAED,MAAM,WAAW,wBAAwB;IACvC,eAAe,EAAE,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC;CACxD;AAED,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,OAAO,CAAC,wBAAwB,CAAC,EAC7E,SAAS,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,EACjC,IAAI,CAAC,EAAE,sBAAsB;YAEgB,IAAI,CAAC,CAAC,EAAE,MAAM,wBAAwB,CAAC;;EAOrF"}
|
|
1
|
+
{"version":3,"file":"contexts.d.ts","sourceRoot":"","sources":["../../../src/inputs/contexts.tsx"],"names":[],"mappings":"AAEA,KAAK,qBAAqB,GAAG;IAC3B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;CACzC,CAAC;AAGF,eAAO,MAAM,yBAAyB,6DAA6B,CAAC;AAGpE,eAAO,MAAM,sBAAsB,8CAA0B,CAAC;AAG9D,eAAO,MAAM,wBAAwB,8CAAmC,CAAC;AAGzE,eAAO,MAAM,oBAAoB,+CAA+B,CAAC;AAEjE,UAAU,sBAAsB;IAC9B,gLAAgL;IAChL,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,wBAAgB,kBAAkB,CAAC,EAAE,YAAY,EAAE,GAAE,sBAA2B;;;;;EAQ/E;AAED,wBAAgB,gBAAgB,4DAE/B;AAED,MAAM,MAAM,2BAA2B,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC;AAKlF,eAAO,MAAM,8BAA8B,oCAFhC,2BAA2B,KAAK,IAAI,cAEqC,CAAC;AAErF,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,SAAS,QAQjF;AAED,MAAM,WAAW,wBAAwB;IACvC,eAAe,EAAE,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC;CACxD;AAED,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,OAAO,CAAC,wBAAwB,CAAC,EAC7E,SAAS,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,EACjC,IAAI,CAAC,EAAE,sBAAsB;YAEgB,IAAI,CAAC,CAAC,EAAE,MAAM,wBAAwB,CAAC;;EAOrF"}
|
|
@@ -30,6 +30,7 @@ declare function customRender(ui: ReactElement, { locale, messages, ...renderOpt
|
|
|
30
30
|
"neptune.Expander.expandAriaLabel": string;
|
|
31
31
|
"neptune.ExpressiveMoneyInput.currency.search.placeholder": string;
|
|
32
32
|
"neptune.ExpressiveMoneyInput.currency.select.currency": string;
|
|
33
|
+
"neptune.Field.characterCount": string;
|
|
33
34
|
"neptune.FlowNavigation.back": string;
|
|
34
35
|
"neptune.Info.ariaLabel": string;
|
|
35
36
|
"neptune.Label.optional": string;
|
|
@@ -117,6 +118,7 @@ declare function customRenderHook(callback: () => unknown, { locale, messages }?
|
|
|
117
118
|
"neptune.Expander.expandAriaLabel": string;
|
|
118
119
|
"neptune.ExpressiveMoneyInput.currency.search.placeholder": string;
|
|
119
120
|
"neptune.ExpressiveMoneyInput.currency.select.currency": string;
|
|
121
|
+
"neptune.Field.characterCount": string;
|
|
120
122
|
"neptune.FlowNavigation.back": string;
|
|
121
123
|
"neptune.Info.ariaLabel": string;
|
|
122
124
|
"neptune.Label.optional": string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/test-utils/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAM,MAAM,OAAO,CAAC;AACzC,OAAO,EAAE,MAAM,EAAc,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAMxD;;;;GAIG;AACH,iBAAS,YAAY,CACnB,EAAE,EAAE,YAAY,EAChB,EAAE,MAAuB,EAAE,QAAa,EAAE,GAAG,aAAa,EAAE
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/test-utils/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAM,MAAM,OAAO,CAAC;AACzC,OAAO,EAAE,MAAM,EAAc,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAMxD;;;;GAIG;AACH,iBAAS,YAAY,CACnB,EAAE,EAAE,YAAY,EAChB,EAAE,MAAuB,EAAE,QAAa,EAAE,GAAG,aAAa,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAAK,GAChE,UAAU,CAAC,OAAO,MAAM,CAAC,CAK3B;AAED;;;GAGG;AACH,iBAAS,gBAAgB,CACvB,QAAQ,EAAE,MAAM,OAAO,EACvB,EAAE,MAAuB,EAAE,QAAa,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAAK,uEAKhD;AAED,cAAc,wBAAwB,CAAC;AACvC,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,YAAY,IAAI,MAAM,EAAE,gBAAgB,IAAI,UAAU,EAAE,SAAS,EAAE,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@transferwise/components",
|
|
3
|
-
"version": "0.0.0-experimental-
|
|
3
|
+
"version": "0.0.0-experimental-cdd318f",
|
|
4
4
|
"description": "Neptune React components",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -112,7 +112,7 @@
|
|
|
112
112
|
"@react-aria/overlays": "^3.32.0",
|
|
113
113
|
"@transferwise/formatting": "^2.14.1",
|
|
114
114
|
"@transferwise/neptune-tokens": "^8.23.0",
|
|
115
|
-
"@transferwise/neptune-validation": "^3.3.
|
|
115
|
+
"@transferwise/neptune-validation": "^3.3.3",
|
|
116
116
|
"clsx": "^2.1.1",
|
|
117
117
|
"commonmark": "^0.31.2",
|
|
118
118
|
"core-js": "^3.49.0",
|
package/src/field/Field.css
CHANGED
|
@@ -1,13 +1,29 @@
|
|
|
1
|
-
.np-field-control
|
|
2
|
-
.np-field__prompt {
|
|
1
|
+
.np-field-control {
|
|
3
2
|
margin-top: 4px;
|
|
4
3
|
margin-top: var(--size-4);
|
|
5
4
|
}
|
|
5
|
+
.np-field-validation {
|
|
6
|
+
display: flex;
|
|
7
|
+
align-items: flex-start;
|
|
8
|
+
margin-top: 4px;
|
|
9
|
+
margin-top: var(--size-4);
|
|
10
|
+
gap: 8px;
|
|
11
|
+
gap: var(--size-8);
|
|
12
|
+
}
|
|
13
|
+
.np-field-textarea-char-counter {
|
|
14
|
+
min-width: 55px;
|
|
15
|
+
text-align: right;
|
|
16
|
+
margin-left: auto;
|
|
17
|
+
padding: 4px 0;
|
|
18
|
+
padding: var(--size-4) 0;
|
|
19
|
+
color: #768e9c;
|
|
20
|
+
color: var(--color-content-tertiary);
|
|
21
|
+
}
|
|
6
22
|
.np-field .form-group--typeahead[class],
|
|
7
23
|
.np-field .np-checkbox-label[class] {
|
|
8
24
|
margin-bottom: 0;
|
|
9
25
|
}
|
|
10
|
-
.np-field:has(.wds-radio-group) .np-
|
|
26
|
+
.np-field:has(.wds-radio-group) .np-field-validation {
|
|
11
27
|
margin-top: 12px;
|
|
12
28
|
margin-top: var(--size-12);
|
|
13
29
|
}
|
package/src/field/Field.less
CHANGED
|
@@ -1,16 +1,30 @@
|
|
|
1
1
|
.np-field {
|
|
2
|
-
&-control
|
|
3
|
-
&__prompt {
|
|
2
|
+
&-control {
|
|
4
3
|
margin-top: var(--size-4);
|
|
5
4
|
}
|
|
6
5
|
|
|
6
|
+
&-validation {
|
|
7
|
+
display: flex;
|
|
8
|
+
align-items: flex-start;
|
|
9
|
+
margin-top: var(--size-4);
|
|
10
|
+
gap: var(--size-8);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
&-textarea-char-counter {
|
|
14
|
+
min-width: 55px;
|
|
15
|
+
text-align: right;
|
|
16
|
+
margin-left: auto;
|
|
17
|
+
padding: var(--size-4) 0;
|
|
18
|
+
color: var(--color-content-tertiary);
|
|
19
|
+
}
|
|
20
|
+
|
|
7
21
|
// @FIXME space between individual fields should be 24px, while some older inputs
|
|
8
22
|
// inject extraneous space.
|
|
9
23
|
.form-group--typeahead[class],
|
|
10
24
|
.np-checkbox-label[class] {
|
|
11
25
|
margin-bottom: 0;
|
|
12
26
|
}
|
|
13
|
-
&:has(.wds-radio-group)
|
|
27
|
+
&:has(.wds-radio-group) &-validation {
|
|
14
28
|
margin-top: var(--size-12);
|
|
15
29
|
}
|
|
16
30
|
}
|
|
@@ -84,7 +84,11 @@ export const Basic = (args: FieldProps) => {
|
|
|
84
84
|
messageIconLabel={args.messageIconLabel}
|
|
85
85
|
messageLoading={args.messageLoading}
|
|
86
86
|
>
|
|
87
|
-
<TextArea
|
|
87
|
+
<TextArea
|
|
88
|
+
maxLength={200}
|
|
89
|
+
value={value}
|
|
90
|
+
onChange={({ target }) => setValue(target.value)}
|
|
91
|
+
/>
|
|
88
92
|
</Field>
|
|
89
93
|
|
|
90
94
|
<Field
|
package/src/field/Field.test.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import Info from '../info/Info';
|
|
2
2
|
import { Input } from '../inputs/Input';
|
|
3
|
+
import { TextArea } from '../inputs/TextArea';
|
|
3
4
|
import { mockMatchMedia, render, screen, userEvent } from '../test-utils';
|
|
4
5
|
|
|
5
6
|
import { Field } from './Field';
|
|
@@ -163,4 +164,93 @@ describe('Field', () => {
|
|
|
163
164
|
expect(screen.getByTestId('InlinePrompt_ProcessIndicator')).toBeInTheDocument();
|
|
164
165
|
expect(screen.getByText('Processing your request')).toBeInTheDocument();
|
|
165
166
|
});
|
|
167
|
+
|
|
168
|
+
describe('TextArea character count', () => {
|
|
169
|
+
it('renders counter when TextArea has maxLength', () => {
|
|
170
|
+
render(
|
|
171
|
+
<Field label="Message">
|
|
172
|
+
<TextArea maxLength={200} value="hello" onChange={() => {}} />
|
|
173
|
+
</Field>,
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
expect(screen.getByText('5/200')).toBeInTheDocument();
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('does not render counter when TextArea has no maxLength', () => {
|
|
180
|
+
render(
|
|
181
|
+
<Field label="Message">
|
|
182
|
+
<TextArea value="hello" onChange={() => {}} />
|
|
183
|
+
</Field>,
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
expect(screen.queryByText(/\/\d+/)).not.toBeInTheDocument();
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('includes counter id in aria-describedby of the textarea', () => {
|
|
190
|
+
render(
|
|
191
|
+
<Field label="Message">
|
|
192
|
+
<TextArea maxLength={200} value="hello" onChange={() => {}} />
|
|
193
|
+
</Field>,
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
const textarea = screen.getByRole('textbox');
|
|
197
|
+
const counter = screen.getByText('5/200');
|
|
198
|
+
expect(textarea).toHaveAttribute('aria-describedby', expect.stringContaining(counter.id));
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it('does not have role=status below 80% threshold', () => {
|
|
202
|
+
render(
|
|
203
|
+
<Field label="Message">
|
|
204
|
+
<TextArea maxLength={200} value="short" onChange={() => {}} />
|
|
205
|
+
</Field>,
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
const counter = screen.getByText('5/200');
|
|
209
|
+
expect(counter).not.toHaveAttribute('role');
|
|
210
|
+
expect(counter).not.toHaveAttribute('aria-live');
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it('has role=status and aria-live=polite at 80% threshold', () => {
|
|
214
|
+
const value = 'x'.repeat(160);
|
|
215
|
+
render(
|
|
216
|
+
<Field label="Message">
|
|
217
|
+
<TextArea maxLength={200} value={value} onChange={() => {}} />
|
|
218
|
+
</Field>,
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
const counter = screen.getByText('160/200');
|
|
222
|
+
expect(counter).toHaveAttribute('role', 'status');
|
|
223
|
+
expect(counter).toHaveAttribute('aria-live', 'polite');
|
|
224
|
+
expect(counter).toHaveAttribute('aria-atomic', 'true');
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
it('updates counter when text changes', () => {
|
|
228
|
+
const { rerender } = render(
|
|
229
|
+
<Field label="Message">
|
|
230
|
+
<TextArea maxLength={200} value="hi" onChange={() => {}} />
|
|
231
|
+
</Field>,
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
expect(screen.getByText('2/200')).toBeInTheDocument();
|
|
235
|
+
|
|
236
|
+
rerender(
|
|
237
|
+
<Field label="Message">
|
|
238
|
+
<TextArea maxLength={200} value="hello world" onChange={() => {}} />
|
|
239
|
+
</Field>,
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
expect(screen.getByText('11/200')).toBeInTheDocument();
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it('provides accessible aria-label on the counter', () => {
|
|
246
|
+
render(
|
|
247
|
+
<Field label="Message">
|
|
248
|
+
<TextArea maxLength={200} value="hello" onChange={() => {}} />
|
|
249
|
+
</Field>,
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
const counter = screen.getByText('5/200');
|
|
253
|
+
expect(counter).toHaveAttribute('aria-label', '5 of 200 characters used');
|
|
254
|
+
});
|
|
255
|
+
});
|
|
166
256
|
});
|