From 51d35a32e05687427f4d9204308f3d068ca5c4e2 Mon Sep 17 00:00:00 2001 From: Robert Snow Date: Tue, 22 Jul 2025 15:01:07 +1000 Subject: [PATCH 1/8] feat: numberfield announce pasted value --- .../@react-aria/numberfield/intl/ar-AE.json | 4 ++- .../@react-aria/numberfield/intl/bg-BG.json | 4 ++- .../@react-aria/numberfield/intl/cs-CZ.json | 4 ++- .../@react-aria/numberfield/intl/da-DK.json | 4 ++- .../@react-aria/numberfield/intl/de-DE.json | 4 ++- .../@react-aria/numberfield/intl/el-GR.json | 4 ++- .../@react-aria/numberfield/intl/en-US.json | 4 ++- .../@react-aria/numberfield/intl/es-ES.json | 4 ++- .../@react-aria/numberfield/intl/et-EE.json | 4 ++- .../@react-aria/numberfield/intl/fi-FI.json | 4 ++- .../@react-aria/numberfield/intl/fr-FR.json | 4 ++- .../@react-aria/numberfield/intl/he-IL.json | 4 ++- .../@react-aria/numberfield/intl/hr-HR.json | 4 ++- .../@react-aria/numberfield/intl/hu-HU.json | 4 ++- .../@react-aria/numberfield/intl/it-IT.json | 4 ++- .../@react-aria/numberfield/intl/ja-JP.json | 4 ++- .../@react-aria/numberfield/intl/ko-KR.json | 4 ++- .../@react-aria/numberfield/intl/lt-LT.json | 4 ++- .../@react-aria/numberfield/intl/lv-LV.json | 4 ++- .../@react-aria/numberfield/intl/nb-NO.json | 4 ++- .../@react-aria/numberfield/intl/nl-NL.json | 4 ++- .../@react-aria/numberfield/intl/pl-PL.json | 4 ++- .../@react-aria/numberfield/intl/pt-BR.json | 4 ++- .../@react-aria/numberfield/intl/pt-PT.json | 4 ++- .../@react-aria/numberfield/intl/ro-RO.json | 4 ++- .../@react-aria/numberfield/intl/ru-RU.json | 4 ++- .../@react-aria/numberfield/intl/sk-SK.json | 4 ++- .../@react-aria/numberfield/intl/sl-SI.json | 4 ++- .../@react-aria/numberfield/intl/sr-SP.json | 4 ++- .../@react-aria/numberfield/intl/sv-SE.json | 4 ++- .../@react-aria/numberfield/intl/tr-TR.json | 4 ++- .../@react-aria/numberfield/intl/uk-UA.json | 4 ++- .../@react-aria/numberfield/intl/zh-CN.json | 4 ++- .../@react-aria/numberfield/intl/zh-TW.json | 4 ++- packages/@react-aria/numberfield/package.json | 1 + .../numberfield/src/useNumberField.ts | 32 +++++++++++++++++++ .../numberfield/src/useNumberFieldState.ts | 7 ++-- .../test/NumberField.test.js | 29 +++++++++++++++++ yarn.lock | 1 + 39 files changed, 170 insertions(+), 36 deletions(-) diff --git a/packages/@react-aria/numberfield/intl/ar-AE.json b/packages/@react-aria/numberfield/intl/ar-AE.json index 84998794627..f5a38677072 100644 --- a/packages/@react-aria/numberfield/intl/ar-AE.json +++ b/packages/@react-aria/numberfield/intl/ar-AE.json @@ -1,5 +1,7 @@ { "decrease": "خفض {fieldLabel}", "increase": "زيادة {fieldLabel}", - "numberField": "حقل رقمي" + "numberField": "حقل رقمي", + "pastedValue": "Pasted value: {value}", + "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" } diff --git a/packages/@react-aria/numberfield/intl/bg-BG.json b/packages/@react-aria/numberfield/intl/bg-BG.json index 916c174865e..f45ecac75c6 100644 --- a/packages/@react-aria/numberfield/intl/bg-BG.json +++ b/packages/@react-aria/numberfield/intl/bg-BG.json @@ -1,5 +1,7 @@ { "decrease": "Намаляване {fieldLabel}", "increase": "Усилване {fieldLabel}", - "numberField": "Номер на полето" + "numberField": "Номер на полето", + "pastedValue": "Pasted value: {value}", + "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" } diff --git a/packages/@react-aria/numberfield/intl/cs-CZ.json b/packages/@react-aria/numberfield/intl/cs-CZ.json index 63c73d72f57..a9aceeeb5c3 100644 --- a/packages/@react-aria/numberfield/intl/cs-CZ.json +++ b/packages/@react-aria/numberfield/intl/cs-CZ.json @@ -1,5 +1,7 @@ { "decrease": "Snížit {fieldLabel}", "increase": "Zvýšit {fieldLabel}", - "numberField": "Číselné pole" + "numberField": "Číselné pole", + "pastedValue": "Pasted value: {value}", + "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" } diff --git a/packages/@react-aria/numberfield/intl/da-DK.json b/packages/@react-aria/numberfield/intl/da-DK.json index 505331be802..08c7793d4ff 100644 --- a/packages/@react-aria/numberfield/intl/da-DK.json +++ b/packages/@react-aria/numberfield/intl/da-DK.json @@ -1,5 +1,7 @@ { "decrease": "Reducer {fieldLabel}", "increase": "Øg {fieldLabel}", - "numberField": "Talfelt" + "numberField": "Talfelt", + "pastedValue": "Pasted value: {value}", + "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" } diff --git a/packages/@react-aria/numberfield/intl/de-DE.json b/packages/@react-aria/numberfield/intl/de-DE.json index e4ff090ffbf..4ce8c555310 100644 --- a/packages/@react-aria/numberfield/intl/de-DE.json +++ b/packages/@react-aria/numberfield/intl/de-DE.json @@ -1,5 +1,7 @@ { "decrease": "{fieldLabel} verringern", "increase": "{fieldLabel} erhöhen", - "numberField": "Nummernfeld" + "numberField": "Nummernfeld", + "pastedValue": "Pasted value: {value}", + "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" } diff --git a/packages/@react-aria/numberfield/intl/el-GR.json b/packages/@react-aria/numberfield/intl/el-GR.json index b60c669d851..103bc39a79d 100644 --- a/packages/@react-aria/numberfield/intl/el-GR.json +++ b/packages/@react-aria/numberfield/intl/el-GR.json @@ -1,5 +1,7 @@ { "decrease": "Μείωση {fieldLabel}", "increase": "Αύξηση {fieldLabel}", - "numberField": "Πεδίο αριθμού" + "numberField": "Πεδίο αριθμού", + "pastedValue": "Pasted value: {value}", + "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" } diff --git a/packages/@react-aria/numberfield/intl/en-US.json b/packages/@react-aria/numberfield/intl/en-US.json index ff6eb41b105..ab2929165e6 100644 --- a/packages/@react-aria/numberfield/intl/en-US.json +++ b/packages/@react-aria/numberfield/intl/en-US.json @@ -1,5 +1,7 @@ { "decrease": "Decrease {fieldLabel}", "increase": "Increase {fieldLabel}", - "numberField": "Number field" + "numberField": "Number field", + "pastedValue": "Pasted value: {value}", + "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" } diff --git a/packages/@react-aria/numberfield/intl/es-ES.json b/packages/@react-aria/numberfield/intl/es-ES.json index 8794c3f99ae..42c0b951ccd 100644 --- a/packages/@react-aria/numberfield/intl/es-ES.json +++ b/packages/@react-aria/numberfield/intl/es-ES.json @@ -1,5 +1,7 @@ { "decrease": "Reducir {fieldLabel}", "increase": "Aumentar {fieldLabel}", - "numberField": "Campo de número" + "numberField": "Campo de número", + "pastedValue": "Pasted value: {value}", + "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" } diff --git a/packages/@react-aria/numberfield/intl/et-EE.json b/packages/@react-aria/numberfield/intl/et-EE.json index 2b077a46f04..837dc4be625 100644 --- a/packages/@react-aria/numberfield/intl/et-EE.json +++ b/packages/@react-aria/numberfield/intl/et-EE.json @@ -1,5 +1,7 @@ { "decrease": "Vähenda {fieldLabel}", "increase": "Suurenda {fieldLabel}", - "numberField": "Numbri väli" + "numberField": "Numbri väli", + "pastedValue": "Pasted value: {value}", + "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" } diff --git a/packages/@react-aria/numberfield/intl/fi-FI.json b/packages/@react-aria/numberfield/intl/fi-FI.json index fac2d7f0fcc..ac981f9f5c7 100644 --- a/packages/@react-aria/numberfield/intl/fi-FI.json +++ b/packages/@react-aria/numberfield/intl/fi-FI.json @@ -1,5 +1,7 @@ { "decrease": "Vähennä {fieldLabel}", "increase": "Lisää {fieldLabel}", - "numberField": "Numerokenttä" + "numberField": "Numerokenttä", + "pastedValue": "Pasted value: {value}", + "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" } diff --git a/packages/@react-aria/numberfield/intl/fr-FR.json b/packages/@react-aria/numberfield/intl/fr-FR.json index 6f0cf0f9773..e2488cdcd38 100644 --- a/packages/@react-aria/numberfield/intl/fr-FR.json +++ b/packages/@react-aria/numberfield/intl/fr-FR.json @@ -1,5 +1,7 @@ { "decrease": "Diminuer {fieldLabel}", "increase": "Augmenter {fieldLabel}", - "numberField": "Champ de nombre" + "numberField": "Champ de nombre", + "pastedValue": "Pasted value: {value}", + "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" } diff --git a/packages/@react-aria/numberfield/intl/he-IL.json b/packages/@react-aria/numberfield/intl/he-IL.json index a6c5a90871b..af677c58221 100644 --- a/packages/@react-aria/numberfield/intl/he-IL.json +++ b/packages/@react-aria/numberfield/intl/he-IL.json @@ -1,5 +1,7 @@ { "decrease": "הקטן {fieldLabel}", "increase": "הגדל {fieldLabel}", - "numberField": "שדה מספר" + "numberField": "שדה מספר", + "pastedValue": "Pasted value: {value}", + "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" } diff --git a/packages/@react-aria/numberfield/intl/hr-HR.json b/packages/@react-aria/numberfield/intl/hr-HR.json index 12a4eba7361..b11007bd444 100644 --- a/packages/@react-aria/numberfield/intl/hr-HR.json +++ b/packages/@react-aria/numberfield/intl/hr-HR.json @@ -1,5 +1,7 @@ { "decrease": "Smanji {fieldLabel}", "increase": "Povećaj {fieldLabel}", - "numberField": "Polje broja" + "numberField": "Polje broja", + "pastedValue": "Pasted value: {value}", + "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" } diff --git a/packages/@react-aria/numberfield/intl/hu-HU.json b/packages/@react-aria/numberfield/intl/hu-HU.json index 67825897391..4764943668c 100644 --- a/packages/@react-aria/numberfield/intl/hu-HU.json +++ b/packages/@react-aria/numberfield/intl/hu-HU.json @@ -1,5 +1,7 @@ { "decrease": "{fieldLabel} csökkentése", "increase": "{fieldLabel} növelése", - "numberField": "Számmező" + "numberField": "Számmező", + "pastedValue": "Pasted value: {value}", + "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" } diff --git a/packages/@react-aria/numberfield/intl/it-IT.json b/packages/@react-aria/numberfield/intl/it-IT.json index 481cd7ac2f5..fd9702af3ac 100644 --- a/packages/@react-aria/numberfield/intl/it-IT.json +++ b/packages/@react-aria/numberfield/intl/it-IT.json @@ -1,5 +1,7 @@ { "decrease": "Riduci {fieldLabel}", "increase": "Aumenta {fieldLabel}", - "numberField": "Campo numero" + "numberField": "Campo numero", + "pastedValue": "Pasted value: {value}", + "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" } diff --git a/packages/@react-aria/numberfield/intl/ja-JP.json b/packages/@react-aria/numberfield/intl/ja-JP.json index 2e078e5aee2..ae3f76407d4 100644 --- a/packages/@react-aria/numberfield/intl/ja-JP.json +++ b/packages/@react-aria/numberfield/intl/ja-JP.json @@ -1,5 +1,7 @@ { "decrease": "{fieldLabel}を縮小", "increase": "{fieldLabel}を拡大", - "numberField": "数値フィールド" + "numberField": "数値フィールド", + "pastedValue": "Pasted value: {value}", + "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" } diff --git a/packages/@react-aria/numberfield/intl/ko-KR.json b/packages/@react-aria/numberfield/intl/ko-KR.json index cc068bc78e4..2b8549d4160 100644 --- a/packages/@react-aria/numberfield/intl/ko-KR.json +++ b/packages/@react-aria/numberfield/intl/ko-KR.json @@ -1,5 +1,7 @@ { "decrease": "{fieldLabel} 감소", "increase": "{fieldLabel} 증가", - "numberField": "번호 필드" + "numberField": "번호 필드", + "pastedValue": "Pasted value: {value}", + "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" } diff --git a/packages/@react-aria/numberfield/intl/lt-LT.json b/packages/@react-aria/numberfield/intl/lt-LT.json index 8e53e5cdea2..b5abaefafbf 100644 --- a/packages/@react-aria/numberfield/intl/lt-LT.json +++ b/packages/@react-aria/numberfield/intl/lt-LT.json @@ -1,5 +1,7 @@ { "decrease": "Sumažinti {fieldLabel}", "increase": "Padidinti {fieldLabel}", - "numberField": "Numerio laukas" + "numberField": "Numerio laukas", + "pastedValue": "Pasted value: {value}", + "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" } diff --git a/packages/@react-aria/numberfield/intl/lv-LV.json b/packages/@react-aria/numberfield/intl/lv-LV.json index 4b9c2b8e5a3..fa1fd7c4e20 100644 --- a/packages/@react-aria/numberfield/intl/lv-LV.json +++ b/packages/@react-aria/numberfield/intl/lv-LV.json @@ -1,5 +1,7 @@ { "decrease": "Samazināšana {fieldLabel}", "increase": "Palielināšana {fieldLabel}", - "numberField": "Skaitļu lauks" + "numberField": "Skaitļu lauks", + "pastedValue": "Pasted value: {value}", + "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" } diff --git a/packages/@react-aria/numberfield/intl/nb-NO.json b/packages/@react-aria/numberfield/intl/nb-NO.json index 276c7b649bc..3c2d464221c 100644 --- a/packages/@react-aria/numberfield/intl/nb-NO.json +++ b/packages/@react-aria/numberfield/intl/nb-NO.json @@ -1,5 +1,7 @@ { "decrease": "Reduser {fieldLabel}", "increase": "Øk {fieldLabel}", - "numberField": "Tallfelt" + "numberField": "Tallfelt", + "pastedValue": "Pasted value: {value}", + "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" } diff --git a/packages/@react-aria/numberfield/intl/nl-NL.json b/packages/@react-aria/numberfield/intl/nl-NL.json index 5c57c2ff533..f21a09778df 100644 --- a/packages/@react-aria/numberfield/intl/nl-NL.json +++ b/packages/@react-aria/numberfield/intl/nl-NL.json @@ -1,5 +1,7 @@ { "decrease": "{fieldLabel} verlagen", "increase": "{fieldLabel} verhogen", - "numberField": "Getalveld" + "numberField": "Getalveld", + "pastedValue": "Pasted value: {value}", + "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" } diff --git a/packages/@react-aria/numberfield/intl/pl-PL.json b/packages/@react-aria/numberfield/intl/pl-PL.json index 4cdf28fbf83..6fb797d6d66 100644 --- a/packages/@react-aria/numberfield/intl/pl-PL.json +++ b/packages/@react-aria/numberfield/intl/pl-PL.json @@ -1,5 +1,7 @@ { "decrease": "Zmniejsz {fieldLabel}", "increase": "Zwiększ {fieldLabel}", - "numberField": "Pole numeru" + "numberField": "Pole numeru", + "pastedValue": "Pasted value: {value}", + "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" } diff --git a/packages/@react-aria/numberfield/intl/pt-BR.json b/packages/@react-aria/numberfield/intl/pt-BR.json index b0740b0fe44..1e18c3509f5 100644 --- a/packages/@react-aria/numberfield/intl/pt-BR.json +++ b/packages/@react-aria/numberfield/intl/pt-BR.json @@ -1,5 +1,7 @@ { "decrease": "Diminuir {fieldLabel}", "increase": "Aumentar {fieldLabel}", - "numberField": "Campo de número" + "numberField": "Campo de número", + "pastedValue": "Pasted value: {value}", + "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" } diff --git a/packages/@react-aria/numberfield/intl/pt-PT.json b/packages/@react-aria/numberfield/intl/pt-PT.json index dbf5ae5146a..6a38d1ab15e 100644 --- a/packages/@react-aria/numberfield/intl/pt-PT.json +++ b/packages/@react-aria/numberfield/intl/pt-PT.json @@ -1,5 +1,7 @@ { "decrease": "Diminuir {fieldLabel}", "increase": "Aumentar {fieldLabel}", - "numberField": "Campo numérico" + "numberField": "Campo numérico", + "pastedValue": "Pasted value: {value}", + "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" } diff --git a/packages/@react-aria/numberfield/intl/ro-RO.json b/packages/@react-aria/numberfield/intl/ro-RO.json index 5db51ed7581..a0d4de7dc2a 100644 --- a/packages/@react-aria/numberfield/intl/ro-RO.json +++ b/packages/@react-aria/numberfield/intl/ro-RO.json @@ -1,5 +1,7 @@ { "decrease": "Scădere {fieldLabel}", "increase": "Creștere {fieldLabel}", - "numberField": "Câmp numeric" + "numberField": "Câmp numeric", + "pastedValue": "Pasted value: {value}", + "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" } diff --git a/packages/@react-aria/numberfield/intl/ru-RU.json b/packages/@react-aria/numberfield/intl/ru-RU.json index 836df8829ea..2c9e7059a15 100644 --- a/packages/@react-aria/numberfield/intl/ru-RU.json +++ b/packages/@react-aria/numberfield/intl/ru-RU.json @@ -1,5 +1,7 @@ { "decrease": "Уменьшение {fieldLabel}", "increase": "Увеличение {fieldLabel}", - "numberField": "Числовое поле" + "numberField": "Числовое поле", + "pastedValue": "Pasted value: {value}", + "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" } diff --git a/packages/@react-aria/numberfield/intl/sk-SK.json b/packages/@react-aria/numberfield/intl/sk-SK.json index 3e54a2df7a4..a890089de76 100644 --- a/packages/@react-aria/numberfield/intl/sk-SK.json +++ b/packages/@react-aria/numberfield/intl/sk-SK.json @@ -1,5 +1,7 @@ { "decrease": "Znížiť {fieldLabel}", "increase": "Zvýšiť {fieldLabel}", - "numberField": "Číselné pole" + "numberField": "Číselné pole", + "pastedValue": "Pasted value: {value}", + "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" } diff --git a/packages/@react-aria/numberfield/intl/sl-SI.json b/packages/@react-aria/numberfield/intl/sl-SI.json index f9fa0ccde3d..9e8f5db6957 100644 --- a/packages/@react-aria/numberfield/intl/sl-SI.json +++ b/packages/@react-aria/numberfield/intl/sl-SI.json @@ -1,5 +1,7 @@ { "decrease": "Upadati {fieldLabel}", "increase": "Povečajte {fieldLabel}", - "numberField": "Številčno polje" + "numberField": "Številčno polje", + "pastedValue": "Pasted value: {value}", + "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" } diff --git a/packages/@react-aria/numberfield/intl/sr-SP.json b/packages/@react-aria/numberfield/intl/sr-SP.json index 12a4eba7361..b11007bd444 100644 --- a/packages/@react-aria/numberfield/intl/sr-SP.json +++ b/packages/@react-aria/numberfield/intl/sr-SP.json @@ -1,5 +1,7 @@ { "decrease": "Smanji {fieldLabel}", "increase": "Povećaj {fieldLabel}", - "numberField": "Polje broja" + "numberField": "Polje broja", + "pastedValue": "Pasted value: {value}", + "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" } diff --git a/packages/@react-aria/numberfield/intl/sv-SE.json b/packages/@react-aria/numberfield/intl/sv-SE.json index 44366b63f61..c6e6493f41c 100644 --- a/packages/@react-aria/numberfield/intl/sv-SE.json +++ b/packages/@react-aria/numberfield/intl/sv-SE.json @@ -1,5 +1,7 @@ { "decrease": "Minska {fieldLabel}", "increase": "Öka {fieldLabel}", - "numberField": "Nummerfält" + "numberField": "Nummerfält", + "pastedValue": "Pasted value: {value}", + "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" } diff --git a/packages/@react-aria/numberfield/intl/tr-TR.json b/packages/@react-aria/numberfield/intl/tr-TR.json index ea2b0ec80d8..4f18d22dfd8 100644 --- a/packages/@react-aria/numberfield/intl/tr-TR.json +++ b/packages/@react-aria/numberfield/intl/tr-TR.json @@ -1,5 +1,7 @@ { "decrease": "{fieldLabel} azalt", "increase": "{fieldLabel} arttır", - "numberField": "Sayı alanı" + "numberField": "Sayı alanı", + "pastedValue": "Pasted value: {value}", + "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" } diff --git a/packages/@react-aria/numberfield/intl/uk-UA.json b/packages/@react-aria/numberfield/intl/uk-UA.json index 02edfbf3a68..60377d0fef3 100644 --- a/packages/@react-aria/numberfield/intl/uk-UA.json +++ b/packages/@react-aria/numberfield/intl/uk-UA.json @@ -1,5 +1,7 @@ { "decrease": "Зменшити {fieldLabel}", "increase": "Збільшити {fieldLabel}", - "numberField": "Поле номера" + "numberField": "Поле номера", + "pastedValue": "Pasted value: {value}", + "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" } diff --git a/packages/@react-aria/numberfield/intl/zh-CN.json b/packages/@react-aria/numberfield/intl/zh-CN.json index a7a42ab37d4..a47416992d9 100644 --- a/packages/@react-aria/numberfield/intl/zh-CN.json +++ b/packages/@react-aria/numberfield/intl/zh-CN.json @@ -1,5 +1,7 @@ { "decrease": "降低 {fieldLabel}", "increase": "提高 {fieldLabel}", - "numberField": "数字字段" + "numberField": "数字字段", + "pastedValue": "Pasted value: {value}", + "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" } diff --git a/packages/@react-aria/numberfield/intl/zh-TW.json b/packages/@react-aria/numberfield/intl/zh-TW.json index a4661fcf92b..cfbb0fdf54b 100644 --- a/packages/@react-aria/numberfield/intl/zh-TW.json +++ b/packages/@react-aria/numberfield/intl/zh-TW.json @@ -1,5 +1,7 @@ { "decrease": "縮小 {fieldLabel}", "increase": "放大 {fieldLabel}", - "numberField": "數字欄位" + "numberField": "數字欄位", + "pastedValue": "Pasted value: {value}", + "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" } diff --git a/packages/@react-aria/numberfield/package.json b/packages/@react-aria/numberfield/package.json index b726521c072..9578afce553 100644 --- a/packages/@react-aria/numberfield/package.json +++ b/packages/@react-aria/numberfield/package.json @@ -28,6 +28,7 @@ "dependencies": { "@react-aria/i18n": "^3.12.10", "@react-aria/interactions": "^3.25.3", + "@react-aria/live-announcer": "^3.4.3", "@react-aria/spinbutton": "^3.6.16", "@react-aria/textfield": "^3.17.5", "@react-aria/utils": "^3.29.1", diff --git a/packages/@react-aria/numberfield/src/useNumberField.ts b/packages/@react-aria/numberfield/src/useNumberField.ts index 08da12b97a5..979d2ffe5ea 100644 --- a/packages/@react-aria/numberfield/src/useNumberField.ts +++ b/packages/@react-aria/numberfield/src/useNumberField.ts @@ -15,6 +15,8 @@ import {AriaNumberFieldProps} from '@react-types/numberfield'; import {chain, filterDOMProps, isAndroid, isIOS, isIPhone, mergeProps, useFormReset, useId} from '@react-aria/utils'; import {DOMAttributes, GroupDOMAttributes, TextInputDOMProps, ValidationResult} from '@react-types/shared'; import { + type ClipboardEvent, + type ClipboardEventHandler, InputHTMLAttributes, LabelHTMLAttributes, RefObject, @@ -33,6 +35,7 @@ import { useNumberFormatter } from '@react-aria/i18n'; import {useSpinButton} from '@react-aria/spinbutton'; +import { announce } from '@react-aria/live-announcer'; export interface NumberFieldAria extends ValidationResult { /** Props for the label element. */ @@ -181,6 +184,34 @@ export function useNumberField(props: AriaNumberFieldProps, state: NumberFieldSt } }; + let onPaste: ClipboardEventHandler = (e: ClipboardEvent) => { + props.onPaste?.(e); + let inputElement = e.target as HTMLInputElement; + if ( + inputElement && + ( + ((inputElement.selectionEnd ?? -1) - (inputElement.selectionStart ?? 0)) === inputElement.value.length + ) + ) { + e.preventDefault(); + let pastedText = e.clipboardData?.getData?.('text/plain')?.trim() ?? ''; + let isValid = state.validate(pastedText); + if (isValid) { + let value = state.parser.parse(pastedText); + let reformattedValue = numberFormatter.format(value); + if (state.validate(reformattedValue)) { + state.setInputValue(reformattedValue); + if (reformattedValue !== pastedText) { + announce(stringFormatter.format('pastedValue', {value: reformattedValue}), 'polite'); + } + } + } else { + state.setInputValue(''); + announce(stringFormatter.format('couldNotParseValue', {value: pastedText}), 'polite'); + } + } + }; + let domProps = filterDOMProps(props); let onKeyDownEnter = useCallback((e) => { if (e.nativeEvent.isComposing) { @@ -223,6 +254,7 @@ export function useNumberField(props: AriaNumberFieldProps, state: NumberFieldSt onFocusChange, onKeyDown: useMemo(() => chain(onKeyDownEnter, onKeyDown), [onKeyDownEnter, onKeyDown]), onKeyUp, + onPaste, description, errorMessage }, state, inputRef); diff --git a/packages/@react-stately/numberfield/src/useNumberFieldState.ts b/packages/@react-stately/numberfield/src/useNumberFieldState.ts index 9902a739c6a..8abee9598b1 100644 --- a/packages/@react-stately/numberfield/src/useNumberFieldState.ts +++ b/packages/@react-stately/numberfield/src/useNumberFieldState.ts @@ -61,7 +61,9 @@ export interface NumberFieldState extends FormValidationState { /** Sets the current value to the `maxValue` if any, and fires `onChange`. */ incrementToMax(): void, /** Sets the current value to the `minValue` if any, and fires `onChange`. */ - decrementToMin(): void + decrementToMin(): void, + /** Attempts to parse a value in the current locale. */ + parser: NumberParser } export interface NumberFieldStateOptions extends NumberFieldProps { @@ -281,7 +283,8 @@ export function useNumberFieldState( setNumberValue, setInputValue, inputValue, - commit + commit, + parser: numberParser }; } diff --git a/packages/react-aria-components/test/NumberField.test.js b/packages/react-aria-components/test/NumberField.test.js index 14c3e0db770..43cd465d5e9 100644 --- a/packages/react-aria-components/test/NumberField.test.js +++ b/packages/react-aria-components/test/NumberField.test.js @@ -10,7 +10,9 @@ * governing permissions and limitations under the License. */ +jest.mock('@react-aria/live-announcer'); import {act, pointerMap, render} from '@react-spectrum/test-utils-internal'; +import {announce} from '@react-aria/live-announcer'; import {Button, FieldError, Group, Input, Label, NumberField, NumberFieldContext, Text} from '../'; import React from 'react'; import userEvent from '@testing-library/user-event'; @@ -183,4 +185,31 @@ describe('NumberField', () => { expect(input).not.toHaveAttribute('aria-describedby'); expect(numberfield).not.toHaveAttribute('data-invalid'); }); + + it('should support pasting', async () => { + let {getByRole} = render(); + let input = getByRole('textbox'); + await user.tab(); + await user.paste('1024'); + expect(input).toHaveValue('1,024'); + expect(announce).toHaveBeenCalledWith('Pasted value: 1,024', 'polite'); + }); + + it('should support pasting into a format', async () => { + let {getByRole} = render(); + let input = getByRole('textbox'); + await user.tab(); + await user.paste('1,024'); + expect(input).toHaveValue('$1,024.00'); + expect(announce).toHaveBeenCalledWith('Pasted value: $1,024.00', 'polite'); + }); + + it('should tell users if their paste failed', async () => { + let {getByRole} = render(); + let input = getByRole('textbox'); + await user.tab(); + await user.paste('$1.00 024 5.6'); + expect(input).toHaveValue(''); + expect(announce).toHaveBeenCalledWith('Could not understand value: $1.00 024 5.6, try another format perhaps', 'polite'); + }); }); diff --git a/yarn.lock b/yarn.lock index 7f61eb2db16..a3806c8204b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5879,6 +5879,7 @@ __metadata: dependencies: "@react-aria/i18n": "npm:^3.12.10" "@react-aria/interactions": "npm:^3.25.3" + "@react-aria/live-announcer": "npm:^3.4.3" "@react-aria/spinbutton": "npm:^3.6.16" "@react-aria/textfield": "npm:^3.17.5" "@react-aria/utils": "npm:^3.29.1" From 00335cb7e5324e5c7bde042cb27ab0dce139ceed Mon Sep 17 00:00:00 2001 From: Robert Snow Date: Tue, 22 Jul 2025 16:13:53 +1000 Subject: [PATCH 2/8] fix tests --- .../numberfield/src/useNumberField.ts | 1 - .../test/NumberField.test.js | 24 ++++++++++++++++--- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/packages/@react-aria/numberfield/src/useNumberField.ts b/packages/@react-aria/numberfield/src/useNumberField.ts index 979d2ffe5ea..d09befd373b 100644 --- a/packages/@react-aria/numberfield/src/useNumberField.ts +++ b/packages/@react-aria/numberfield/src/useNumberField.ts @@ -206,7 +206,6 @@ export function useNumberField(props: AriaNumberFieldProps, state: NumberFieldSt } } } else { - state.setInputValue(''); announce(stringFormatter.format('couldNotParseValue', {value: pastedText}), 'polite'); } } diff --git a/packages/react-aria-components/test/NumberField.test.js b/packages/react-aria-components/test/NumberField.test.js index 43cd465d5e9..fafab6e4d38 100644 --- a/packages/react-aria-components/test/NumberField.test.js +++ b/packages/react-aria-components/test/NumberField.test.js @@ -186,28 +186,46 @@ describe('NumberField', () => { expect(numberfield).not.toHaveAttribute('data-invalid'); }); + it('supports onChange', async () => { + let onChange = jest.fn((e) => console.log('onChange', e)); + let {getByRole} = render(); + let input = getByRole('textbox'); + await user.tab(); + await user.clear(input); + await user.keyboard('1024'); + await user.keyboard('{Enter}'); + expect(onChange).toHaveBeenCalledWith(1024); + }); + it('should support pasting', async () => { - let {getByRole} = render(); + let {getByRole} = render(); let input = getByRole('textbox'); await user.tab(); + await user.clear(input); await user.paste('1024'); expect(input).toHaveValue('1,024'); expect(announce).toHaveBeenCalledWith('Pasted value: 1,024', 'polite'); }); it('should support pasting into a format', async () => { - let {getByRole} = render(); + let onChange = jest.fn(); + let {getByRole} = render(); let input = getByRole('textbox'); await user.tab(); + await user.clear(input); await user.paste('1,024'); expect(input).toHaveValue('$1,024.00'); expect(announce).toHaveBeenCalledWith('Pasted value: $1,024.00', 'polite'); + expect(onChange).not.toHaveBeenCalled(); + await user.keyboard('{Enter}'); + expect(onChange).toHaveBeenCalledWith(1024); }); it('should tell users if their paste failed', async () => { - let {getByRole} = render(); + let {getByRole} = render(); let input = getByRole('textbox'); await user.tab(); + await user.clear(input); await user.paste('$1.00 024 5.6'); expect(input).toHaveValue(''); expect(announce).toHaveBeenCalledWith('Could not understand value: $1.00 024 5.6, try another format perhaps', 'polite'); From 54001e58f16043f9d46c057f8d5036dbb76b8eeb Mon Sep 17 00:00:00 2001 From: Robert Snow Date: Tue, 22 Jul 2025 16:19:40 +1000 Subject: [PATCH 3/8] fix lint --- packages/@react-aria/numberfield/src/useNumberField.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@react-aria/numberfield/src/useNumberField.ts b/packages/@react-aria/numberfield/src/useNumberField.ts index d09befd373b..b08fa0f4557 100644 --- a/packages/@react-aria/numberfield/src/useNumberField.ts +++ b/packages/@react-aria/numberfield/src/useNumberField.ts @@ -10,10 +10,10 @@ * governing permissions and limitations under the License. */ +import {announce} from '@react-aria/live-announcer'; import {AriaButtonProps} from '@react-types/button'; import {AriaNumberFieldProps} from '@react-types/numberfield'; import {chain, filterDOMProps, isAndroid, isIOS, isIPhone, mergeProps, useFormReset, useId} from '@react-aria/utils'; -import {DOMAttributes, GroupDOMAttributes, TextInputDOMProps, ValidationResult} from '@react-types/shared'; import { type ClipboardEvent, type ClipboardEventHandler, @@ -25,6 +25,7 @@ import { useState } from 'react'; // @ts-ignore +import {DOMAttributes, GroupDOMAttributes, TextInputDOMProps, ValidationResult} from '@react-types/shared'; import intlMessages from '../intl/*.json'; import {NumberFieldState} from '@react-stately/numberfield'; import {privateValidationStateProp} from '@react-stately/form'; @@ -35,7 +36,6 @@ import { useNumberFormatter } from '@react-aria/i18n'; import {useSpinButton} from '@react-aria/spinbutton'; -import { announce } from '@react-aria/live-announcer'; export interface NumberFieldAria extends ValidationResult { /** Props for the label element. */ From 6111f5ac23f4f3cbe48e5492f2a7fe46f1231bb0 Mon Sep 17 00:00:00 2001 From: Robert Snow Date: Tue, 22 Jul 2025 16:36:23 +1000 Subject: [PATCH 4/8] add controlled tests --- .../test/NumberField.test.js | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/packages/react-aria-components/test/NumberField.test.js b/packages/react-aria-components/test/NumberField.test.js index fafab6e4d38..2df34fd645e 100644 --- a/packages/react-aria-components/test/NumberField.test.js +++ b/packages/react-aria-components/test/NumberField.test.js @@ -14,7 +14,7 @@ jest.mock('@react-aria/live-announcer'); import {act, pointerMap, render} from '@react-spectrum/test-utils-internal'; import {announce} from '@react-aria/live-announcer'; import {Button, FieldError, Group, Input, Label, NumberField, NumberFieldContext, Text} from '../'; -import React from 'react'; +import React, { useState } from 'react'; import userEvent from '@testing-library/user-event'; let TestNumberField = (props) => ( @@ -187,7 +187,7 @@ describe('NumberField', () => { }); it('supports onChange', async () => { - let onChange = jest.fn((e) => console.log('onChange', e)); + let onChange = jest.fn(); let {getByRole} = render(); let input = getByRole('textbox'); await user.tab(); @@ -230,4 +230,29 @@ describe('NumberField', () => { expect(input).toHaveValue(''); expect(announce).toHaveBeenCalledWith('Could not understand value: $1.00 024 5.6, try another format perhaps', 'polite'); }); + + it('should support paste announcements for a controlled numberfield', async () => { + function ControlledNumberField({value, ...props}) { + let [numberValue, setNumberValue] = useState(value); + return ; + } + let {getByRole} = render(); + let input = getByRole('textbox'); + await user.tab(); + await user.clear(input); + await user.paste('1024'); + expect(input).toHaveValue('1,024'); + expect(announce).toHaveBeenCalledWith('Pasted value: 1,024', 'polite'); + }); + + it('should not change the input value if the new value is not accepted', async () => { + let {getByRole} = render(); + let input = getByRole('textbox'); + await user.tab(); + await user.clear(input); + await user.paste('1024'); + expect(input).toHaveValue('1,024'); + await user.keyboard('{Enter}'); + expect(input).toHaveValue('200'); + }); }); From b10b1bb9a40f8241bff0a189c38c9d59b5d6a02f Mon Sep 17 00:00:00 2001 From: Robert Snow Date: Tue, 22 Jul 2025 16:43:27 +1000 Subject: [PATCH 5/8] fix lint for real --- packages/@react-aria/numberfield/src/useNumberField.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@react-aria/numberfield/src/useNumberField.ts b/packages/@react-aria/numberfield/src/useNumberField.ts index b08fa0f4557..e590612cc91 100644 --- a/packages/@react-aria/numberfield/src/useNumberField.ts +++ b/packages/@react-aria/numberfield/src/useNumberField.ts @@ -24,8 +24,8 @@ import { useMemo, useState } from 'react'; -// @ts-ignore import {DOMAttributes, GroupDOMAttributes, TextInputDOMProps, ValidationResult} from '@react-types/shared'; +// @ts-ignore import intlMessages from '../intl/*.json'; import {NumberFieldState} from '@react-stately/numberfield'; import {privateValidationStateProp} from '@react-stately/form'; From a4056ca3931628ea8b3d8b0e8246df84093d426a Mon Sep 17 00:00:00 2001 From: Robert Snow Date: Thu, 24 Jul 2025 13:00:51 +1000 Subject: [PATCH 6/8] fix lint --- packages/react-aria-components/test/NumberField.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-aria-components/test/NumberField.test.js b/packages/react-aria-components/test/NumberField.test.js index 2df34fd645e..05a494d2680 100644 --- a/packages/react-aria-components/test/NumberField.test.js +++ b/packages/react-aria-components/test/NumberField.test.js @@ -14,7 +14,7 @@ jest.mock('@react-aria/live-announcer'); import {act, pointerMap, render} from '@react-spectrum/test-utils-internal'; import {announce} from '@react-aria/live-announcer'; import {Button, FieldError, Group, Input, Label, NumberField, NumberFieldContext, Text} from '../'; -import React, { useState } from 'react'; +import React, {useState} from 'react'; import userEvent from '@testing-library/user-event'; let TestNumberField = (props) => ( From 2032fb9b7197767fde97468c4be13a7c4092ff4c Mon Sep 17 00:00:00 2001 From: Robert Snow Date: Fri, 22 Aug 2025 13:16:36 +1000 Subject: [PATCH 7/8] use better blur announcement and fix paste behaviour not to parse out of turn --- .../@react-aria/numberfield/intl/ar-AE.json | 4 +- .../@react-aria/numberfield/intl/bg-BG.json | 4 +- .../@react-aria/numberfield/intl/cs-CZ.json | 4 +- .../@react-aria/numberfield/intl/da-DK.json | 4 +- .../@react-aria/numberfield/intl/de-DE.json | 4 +- .../@react-aria/numberfield/intl/el-GR.json | 4 +- .../@react-aria/numberfield/intl/en-US.json | 4 +- .../@react-aria/numberfield/intl/es-ES.json | 4 +- .../@react-aria/numberfield/intl/et-EE.json | 4 +- .../@react-aria/numberfield/intl/fi-FI.json | 4 +- .../@react-aria/numberfield/intl/fr-FR.json | 4 +- .../@react-aria/numberfield/intl/he-IL.json | 4 +- .../@react-aria/numberfield/intl/hr-HR.json | 4 +- .../@react-aria/numberfield/intl/hu-HU.json | 4 +- .../@react-aria/numberfield/intl/it-IT.json | 4 +- .../@react-aria/numberfield/intl/ja-JP.json | 4 +- .../@react-aria/numberfield/intl/ko-KR.json | 4 +- .../@react-aria/numberfield/intl/lt-LT.json | 4 +- .../@react-aria/numberfield/intl/lv-LV.json | 4 +- .../@react-aria/numberfield/intl/nb-NO.json | 4 +- .../@react-aria/numberfield/intl/nl-NL.json | 4 +- .../@react-aria/numberfield/intl/pl-PL.json | 4 +- .../@react-aria/numberfield/intl/pt-BR.json | 4 +- .../@react-aria/numberfield/intl/pt-PT.json | 4 +- .../@react-aria/numberfield/intl/ro-RO.json | 4 +- .../@react-aria/numberfield/intl/ru-RU.json | 4 +- .../@react-aria/numberfield/intl/sk-SK.json | 4 +- .../@react-aria/numberfield/intl/sl-SI.json | 4 +- .../@react-aria/numberfield/intl/sr-SP.json | 4 +- .../@react-aria/numberfield/intl/sv-SE.json | 4 +- .../@react-aria/numberfield/intl/tr-TR.json | 4 +- .../@react-aria/numberfield/intl/uk-UA.json | 4 +- .../@react-aria/numberfield/intl/zh-CN.json | 4 +- .../@react-aria/numberfield/intl/zh-TW.json | 4 +- .../numberfield/src/useNumberField.ts | 45 ++++++++++--------- .../numberfield/test/NumberField.test.js | 10 +++-- .../numberfield/src/useNumberFieldState.ts | 25 ++++++----- .../test/NumberField.test.js | 42 ++--------------- 38 files changed, 83 insertions(+), 175 deletions(-) diff --git a/packages/@react-aria/numberfield/intl/ar-AE.json b/packages/@react-aria/numberfield/intl/ar-AE.json index f5a38677072..84998794627 100644 --- a/packages/@react-aria/numberfield/intl/ar-AE.json +++ b/packages/@react-aria/numberfield/intl/ar-AE.json @@ -1,7 +1,5 @@ { "decrease": "خفض {fieldLabel}", "increase": "زيادة {fieldLabel}", - "numberField": "حقل رقمي", - "pastedValue": "Pasted value: {value}", - "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" + "numberField": "حقل رقمي" } diff --git a/packages/@react-aria/numberfield/intl/bg-BG.json b/packages/@react-aria/numberfield/intl/bg-BG.json index f45ecac75c6..916c174865e 100644 --- a/packages/@react-aria/numberfield/intl/bg-BG.json +++ b/packages/@react-aria/numberfield/intl/bg-BG.json @@ -1,7 +1,5 @@ { "decrease": "Намаляване {fieldLabel}", "increase": "Усилване {fieldLabel}", - "numberField": "Номер на полето", - "pastedValue": "Pasted value: {value}", - "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" + "numberField": "Номер на полето" } diff --git a/packages/@react-aria/numberfield/intl/cs-CZ.json b/packages/@react-aria/numberfield/intl/cs-CZ.json index a9aceeeb5c3..63c73d72f57 100644 --- a/packages/@react-aria/numberfield/intl/cs-CZ.json +++ b/packages/@react-aria/numberfield/intl/cs-CZ.json @@ -1,7 +1,5 @@ { "decrease": "Snížit {fieldLabel}", "increase": "Zvýšit {fieldLabel}", - "numberField": "Číselné pole", - "pastedValue": "Pasted value: {value}", - "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" + "numberField": "Číselné pole" } diff --git a/packages/@react-aria/numberfield/intl/da-DK.json b/packages/@react-aria/numberfield/intl/da-DK.json index 08c7793d4ff..505331be802 100644 --- a/packages/@react-aria/numberfield/intl/da-DK.json +++ b/packages/@react-aria/numberfield/intl/da-DK.json @@ -1,7 +1,5 @@ { "decrease": "Reducer {fieldLabel}", "increase": "Øg {fieldLabel}", - "numberField": "Talfelt", - "pastedValue": "Pasted value: {value}", - "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" + "numberField": "Talfelt" } diff --git a/packages/@react-aria/numberfield/intl/de-DE.json b/packages/@react-aria/numberfield/intl/de-DE.json index 4ce8c555310..e4ff090ffbf 100644 --- a/packages/@react-aria/numberfield/intl/de-DE.json +++ b/packages/@react-aria/numberfield/intl/de-DE.json @@ -1,7 +1,5 @@ { "decrease": "{fieldLabel} verringern", "increase": "{fieldLabel} erhöhen", - "numberField": "Nummernfeld", - "pastedValue": "Pasted value: {value}", - "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" + "numberField": "Nummernfeld" } diff --git a/packages/@react-aria/numberfield/intl/el-GR.json b/packages/@react-aria/numberfield/intl/el-GR.json index 103bc39a79d..b60c669d851 100644 --- a/packages/@react-aria/numberfield/intl/el-GR.json +++ b/packages/@react-aria/numberfield/intl/el-GR.json @@ -1,7 +1,5 @@ { "decrease": "Μείωση {fieldLabel}", "increase": "Αύξηση {fieldLabel}", - "numberField": "Πεδίο αριθμού", - "pastedValue": "Pasted value: {value}", - "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" + "numberField": "Πεδίο αριθμού" } diff --git a/packages/@react-aria/numberfield/intl/en-US.json b/packages/@react-aria/numberfield/intl/en-US.json index ab2929165e6..ff6eb41b105 100644 --- a/packages/@react-aria/numberfield/intl/en-US.json +++ b/packages/@react-aria/numberfield/intl/en-US.json @@ -1,7 +1,5 @@ { "decrease": "Decrease {fieldLabel}", "increase": "Increase {fieldLabel}", - "numberField": "Number field", - "pastedValue": "Pasted value: {value}", - "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" + "numberField": "Number field" } diff --git a/packages/@react-aria/numberfield/intl/es-ES.json b/packages/@react-aria/numberfield/intl/es-ES.json index 42c0b951ccd..8794c3f99ae 100644 --- a/packages/@react-aria/numberfield/intl/es-ES.json +++ b/packages/@react-aria/numberfield/intl/es-ES.json @@ -1,7 +1,5 @@ { "decrease": "Reducir {fieldLabel}", "increase": "Aumentar {fieldLabel}", - "numberField": "Campo de número", - "pastedValue": "Pasted value: {value}", - "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" + "numberField": "Campo de número" } diff --git a/packages/@react-aria/numberfield/intl/et-EE.json b/packages/@react-aria/numberfield/intl/et-EE.json index 837dc4be625..2b077a46f04 100644 --- a/packages/@react-aria/numberfield/intl/et-EE.json +++ b/packages/@react-aria/numberfield/intl/et-EE.json @@ -1,7 +1,5 @@ { "decrease": "Vähenda {fieldLabel}", "increase": "Suurenda {fieldLabel}", - "numberField": "Numbri väli", - "pastedValue": "Pasted value: {value}", - "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" + "numberField": "Numbri väli" } diff --git a/packages/@react-aria/numberfield/intl/fi-FI.json b/packages/@react-aria/numberfield/intl/fi-FI.json index ac981f9f5c7..fac2d7f0fcc 100644 --- a/packages/@react-aria/numberfield/intl/fi-FI.json +++ b/packages/@react-aria/numberfield/intl/fi-FI.json @@ -1,7 +1,5 @@ { "decrease": "Vähennä {fieldLabel}", "increase": "Lisää {fieldLabel}", - "numberField": "Numerokenttä", - "pastedValue": "Pasted value: {value}", - "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" + "numberField": "Numerokenttä" } diff --git a/packages/@react-aria/numberfield/intl/fr-FR.json b/packages/@react-aria/numberfield/intl/fr-FR.json index e2488cdcd38..6f0cf0f9773 100644 --- a/packages/@react-aria/numberfield/intl/fr-FR.json +++ b/packages/@react-aria/numberfield/intl/fr-FR.json @@ -1,7 +1,5 @@ { "decrease": "Diminuer {fieldLabel}", "increase": "Augmenter {fieldLabel}", - "numberField": "Champ de nombre", - "pastedValue": "Pasted value: {value}", - "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" + "numberField": "Champ de nombre" } diff --git a/packages/@react-aria/numberfield/intl/he-IL.json b/packages/@react-aria/numberfield/intl/he-IL.json index af677c58221..a6c5a90871b 100644 --- a/packages/@react-aria/numberfield/intl/he-IL.json +++ b/packages/@react-aria/numberfield/intl/he-IL.json @@ -1,7 +1,5 @@ { "decrease": "הקטן {fieldLabel}", "increase": "הגדל {fieldLabel}", - "numberField": "שדה מספר", - "pastedValue": "Pasted value: {value}", - "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" + "numberField": "שדה מספר" } diff --git a/packages/@react-aria/numberfield/intl/hr-HR.json b/packages/@react-aria/numberfield/intl/hr-HR.json index b11007bd444..12a4eba7361 100644 --- a/packages/@react-aria/numberfield/intl/hr-HR.json +++ b/packages/@react-aria/numberfield/intl/hr-HR.json @@ -1,7 +1,5 @@ { "decrease": "Smanji {fieldLabel}", "increase": "Povećaj {fieldLabel}", - "numberField": "Polje broja", - "pastedValue": "Pasted value: {value}", - "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" + "numberField": "Polje broja" } diff --git a/packages/@react-aria/numberfield/intl/hu-HU.json b/packages/@react-aria/numberfield/intl/hu-HU.json index 4764943668c..67825897391 100644 --- a/packages/@react-aria/numberfield/intl/hu-HU.json +++ b/packages/@react-aria/numberfield/intl/hu-HU.json @@ -1,7 +1,5 @@ { "decrease": "{fieldLabel} csökkentése", "increase": "{fieldLabel} növelése", - "numberField": "Számmező", - "pastedValue": "Pasted value: {value}", - "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" + "numberField": "Számmező" } diff --git a/packages/@react-aria/numberfield/intl/it-IT.json b/packages/@react-aria/numberfield/intl/it-IT.json index fd9702af3ac..481cd7ac2f5 100644 --- a/packages/@react-aria/numberfield/intl/it-IT.json +++ b/packages/@react-aria/numberfield/intl/it-IT.json @@ -1,7 +1,5 @@ { "decrease": "Riduci {fieldLabel}", "increase": "Aumenta {fieldLabel}", - "numberField": "Campo numero", - "pastedValue": "Pasted value: {value}", - "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" + "numberField": "Campo numero" } diff --git a/packages/@react-aria/numberfield/intl/ja-JP.json b/packages/@react-aria/numberfield/intl/ja-JP.json index ae3f76407d4..2e078e5aee2 100644 --- a/packages/@react-aria/numberfield/intl/ja-JP.json +++ b/packages/@react-aria/numberfield/intl/ja-JP.json @@ -1,7 +1,5 @@ { "decrease": "{fieldLabel}を縮小", "increase": "{fieldLabel}を拡大", - "numberField": "数値フィールド", - "pastedValue": "Pasted value: {value}", - "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" + "numberField": "数値フィールド" } diff --git a/packages/@react-aria/numberfield/intl/ko-KR.json b/packages/@react-aria/numberfield/intl/ko-KR.json index 2b8549d4160..cc068bc78e4 100644 --- a/packages/@react-aria/numberfield/intl/ko-KR.json +++ b/packages/@react-aria/numberfield/intl/ko-KR.json @@ -1,7 +1,5 @@ { "decrease": "{fieldLabel} 감소", "increase": "{fieldLabel} 증가", - "numberField": "번호 필드", - "pastedValue": "Pasted value: {value}", - "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" + "numberField": "번호 필드" } diff --git a/packages/@react-aria/numberfield/intl/lt-LT.json b/packages/@react-aria/numberfield/intl/lt-LT.json index b5abaefafbf..8e53e5cdea2 100644 --- a/packages/@react-aria/numberfield/intl/lt-LT.json +++ b/packages/@react-aria/numberfield/intl/lt-LT.json @@ -1,7 +1,5 @@ { "decrease": "Sumažinti {fieldLabel}", "increase": "Padidinti {fieldLabel}", - "numberField": "Numerio laukas", - "pastedValue": "Pasted value: {value}", - "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" + "numberField": "Numerio laukas" } diff --git a/packages/@react-aria/numberfield/intl/lv-LV.json b/packages/@react-aria/numberfield/intl/lv-LV.json index fa1fd7c4e20..4b9c2b8e5a3 100644 --- a/packages/@react-aria/numberfield/intl/lv-LV.json +++ b/packages/@react-aria/numberfield/intl/lv-LV.json @@ -1,7 +1,5 @@ { "decrease": "Samazināšana {fieldLabel}", "increase": "Palielināšana {fieldLabel}", - "numberField": "Skaitļu lauks", - "pastedValue": "Pasted value: {value}", - "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" + "numberField": "Skaitļu lauks" } diff --git a/packages/@react-aria/numberfield/intl/nb-NO.json b/packages/@react-aria/numberfield/intl/nb-NO.json index 3c2d464221c..276c7b649bc 100644 --- a/packages/@react-aria/numberfield/intl/nb-NO.json +++ b/packages/@react-aria/numberfield/intl/nb-NO.json @@ -1,7 +1,5 @@ { "decrease": "Reduser {fieldLabel}", "increase": "Øk {fieldLabel}", - "numberField": "Tallfelt", - "pastedValue": "Pasted value: {value}", - "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" + "numberField": "Tallfelt" } diff --git a/packages/@react-aria/numberfield/intl/nl-NL.json b/packages/@react-aria/numberfield/intl/nl-NL.json index f21a09778df..5c57c2ff533 100644 --- a/packages/@react-aria/numberfield/intl/nl-NL.json +++ b/packages/@react-aria/numberfield/intl/nl-NL.json @@ -1,7 +1,5 @@ { "decrease": "{fieldLabel} verlagen", "increase": "{fieldLabel} verhogen", - "numberField": "Getalveld", - "pastedValue": "Pasted value: {value}", - "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" + "numberField": "Getalveld" } diff --git a/packages/@react-aria/numberfield/intl/pl-PL.json b/packages/@react-aria/numberfield/intl/pl-PL.json index 6fb797d6d66..4cdf28fbf83 100644 --- a/packages/@react-aria/numberfield/intl/pl-PL.json +++ b/packages/@react-aria/numberfield/intl/pl-PL.json @@ -1,7 +1,5 @@ { "decrease": "Zmniejsz {fieldLabel}", "increase": "Zwiększ {fieldLabel}", - "numberField": "Pole numeru", - "pastedValue": "Pasted value: {value}", - "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" + "numberField": "Pole numeru" } diff --git a/packages/@react-aria/numberfield/intl/pt-BR.json b/packages/@react-aria/numberfield/intl/pt-BR.json index 1e18c3509f5..b0740b0fe44 100644 --- a/packages/@react-aria/numberfield/intl/pt-BR.json +++ b/packages/@react-aria/numberfield/intl/pt-BR.json @@ -1,7 +1,5 @@ { "decrease": "Diminuir {fieldLabel}", "increase": "Aumentar {fieldLabel}", - "numberField": "Campo de número", - "pastedValue": "Pasted value: {value}", - "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" + "numberField": "Campo de número" } diff --git a/packages/@react-aria/numberfield/intl/pt-PT.json b/packages/@react-aria/numberfield/intl/pt-PT.json index 6a38d1ab15e..dbf5ae5146a 100644 --- a/packages/@react-aria/numberfield/intl/pt-PT.json +++ b/packages/@react-aria/numberfield/intl/pt-PT.json @@ -1,7 +1,5 @@ { "decrease": "Diminuir {fieldLabel}", "increase": "Aumentar {fieldLabel}", - "numberField": "Campo numérico", - "pastedValue": "Pasted value: {value}", - "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" + "numberField": "Campo numérico" } diff --git a/packages/@react-aria/numberfield/intl/ro-RO.json b/packages/@react-aria/numberfield/intl/ro-RO.json index a0d4de7dc2a..5db51ed7581 100644 --- a/packages/@react-aria/numberfield/intl/ro-RO.json +++ b/packages/@react-aria/numberfield/intl/ro-RO.json @@ -1,7 +1,5 @@ { "decrease": "Scădere {fieldLabel}", "increase": "Creștere {fieldLabel}", - "numberField": "Câmp numeric", - "pastedValue": "Pasted value: {value}", - "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" + "numberField": "Câmp numeric" } diff --git a/packages/@react-aria/numberfield/intl/ru-RU.json b/packages/@react-aria/numberfield/intl/ru-RU.json index 2c9e7059a15..836df8829ea 100644 --- a/packages/@react-aria/numberfield/intl/ru-RU.json +++ b/packages/@react-aria/numberfield/intl/ru-RU.json @@ -1,7 +1,5 @@ { "decrease": "Уменьшение {fieldLabel}", "increase": "Увеличение {fieldLabel}", - "numberField": "Числовое поле", - "pastedValue": "Pasted value: {value}", - "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" + "numberField": "Числовое поле" } diff --git a/packages/@react-aria/numberfield/intl/sk-SK.json b/packages/@react-aria/numberfield/intl/sk-SK.json index a890089de76..3e54a2df7a4 100644 --- a/packages/@react-aria/numberfield/intl/sk-SK.json +++ b/packages/@react-aria/numberfield/intl/sk-SK.json @@ -1,7 +1,5 @@ { "decrease": "Znížiť {fieldLabel}", "increase": "Zvýšiť {fieldLabel}", - "numberField": "Číselné pole", - "pastedValue": "Pasted value: {value}", - "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" + "numberField": "Číselné pole" } diff --git a/packages/@react-aria/numberfield/intl/sl-SI.json b/packages/@react-aria/numberfield/intl/sl-SI.json index 9e8f5db6957..f9fa0ccde3d 100644 --- a/packages/@react-aria/numberfield/intl/sl-SI.json +++ b/packages/@react-aria/numberfield/intl/sl-SI.json @@ -1,7 +1,5 @@ { "decrease": "Upadati {fieldLabel}", "increase": "Povečajte {fieldLabel}", - "numberField": "Številčno polje", - "pastedValue": "Pasted value: {value}", - "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" + "numberField": "Številčno polje" } diff --git a/packages/@react-aria/numberfield/intl/sr-SP.json b/packages/@react-aria/numberfield/intl/sr-SP.json index b11007bd444..12a4eba7361 100644 --- a/packages/@react-aria/numberfield/intl/sr-SP.json +++ b/packages/@react-aria/numberfield/intl/sr-SP.json @@ -1,7 +1,5 @@ { "decrease": "Smanji {fieldLabel}", "increase": "Povećaj {fieldLabel}", - "numberField": "Polje broja", - "pastedValue": "Pasted value: {value}", - "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" + "numberField": "Polje broja" } diff --git a/packages/@react-aria/numberfield/intl/sv-SE.json b/packages/@react-aria/numberfield/intl/sv-SE.json index c6e6493f41c..44366b63f61 100644 --- a/packages/@react-aria/numberfield/intl/sv-SE.json +++ b/packages/@react-aria/numberfield/intl/sv-SE.json @@ -1,7 +1,5 @@ { "decrease": "Minska {fieldLabel}", "increase": "Öka {fieldLabel}", - "numberField": "Nummerfält", - "pastedValue": "Pasted value: {value}", - "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" + "numberField": "Nummerfält" } diff --git a/packages/@react-aria/numberfield/intl/tr-TR.json b/packages/@react-aria/numberfield/intl/tr-TR.json index 4f18d22dfd8..ea2b0ec80d8 100644 --- a/packages/@react-aria/numberfield/intl/tr-TR.json +++ b/packages/@react-aria/numberfield/intl/tr-TR.json @@ -1,7 +1,5 @@ { "decrease": "{fieldLabel} azalt", "increase": "{fieldLabel} arttır", - "numberField": "Sayı alanı", - "pastedValue": "Pasted value: {value}", - "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" + "numberField": "Sayı alanı" } diff --git a/packages/@react-aria/numberfield/intl/uk-UA.json b/packages/@react-aria/numberfield/intl/uk-UA.json index 60377d0fef3..02edfbf3a68 100644 --- a/packages/@react-aria/numberfield/intl/uk-UA.json +++ b/packages/@react-aria/numberfield/intl/uk-UA.json @@ -1,7 +1,5 @@ { "decrease": "Зменшити {fieldLabel}", "increase": "Збільшити {fieldLabel}", - "numberField": "Поле номера", - "pastedValue": "Pasted value: {value}", - "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" + "numberField": "Поле номера" } diff --git a/packages/@react-aria/numberfield/intl/zh-CN.json b/packages/@react-aria/numberfield/intl/zh-CN.json index a47416992d9..a7a42ab37d4 100644 --- a/packages/@react-aria/numberfield/intl/zh-CN.json +++ b/packages/@react-aria/numberfield/intl/zh-CN.json @@ -1,7 +1,5 @@ { "decrease": "降低 {fieldLabel}", "increase": "提高 {fieldLabel}", - "numberField": "数字字段", - "pastedValue": "Pasted value: {value}", - "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" + "numberField": "数字字段" } diff --git a/packages/@react-aria/numberfield/intl/zh-TW.json b/packages/@react-aria/numberfield/intl/zh-TW.json index cfbb0fdf54b..a4661fcf92b 100644 --- a/packages/@react-aria/numberfield/intl/zh-TW.json +++ b/packages/@react-aria/numberfield/intl/zh-TW.json @@ -1,7 +1,5 @@ { "decrease": "縮小 {fieldLabel}", "increase": "放大 {fieldLabel}", - "numberField": "數字欄位", - "pastedValue": "Pasted value: {value}", - "couldNotParseValue": "Could not understand value: {value}, try another format perhaps" + "numberField": "數字欄位" } diff --git a/packages/@react-aria/numberfield/src/useNumberField.ts b/packages/@react-aria/numberfield/src/useNumberField.ts index e590612cc91..4d662dec154 100644 --- a/packages/@react-aria/numberfield/src/useNumberField.ts +++ b/packages/@react-aria/numberfield/src/useNumberField.ts @@ -36,6 +36,7 @@ import { useNumberFormatter } from '@react-aria/i18n'; import {useSpinButton} from '@react-aria/spinbutton'; +import { flushSync } from 'react-dom'; export interface NumberFieldAria extends ValidationResult { /** Props for the label element. */ @@ -94,12 +95,24 @@ export function useNumberField(props: AriaNumberFieldProps, state: NumberFieldSt } = state; const stringFormatter = useLocalizedStringFormatter(intlMessages, '@react-aria/numberfield'); + let commitAndAnnounce = useCallback(() => { + let oldValue = inputRef.current?.value ?? ''; + // Set input value to normalized valid value + flushSync(() => { + commit(); + }); + // Note: this announcement will be skipped if the user is keyboard navigating to a new + // focusable element because that target will be announced instead, even though this is + // assertive. This is expected VO behaviour. + if (inputRef.current?.value !== oldValue) { + announce(inputRef.current?.value ?? '', 'assertive'); + } + }, [commit, inputRef]); let inputId = useId(id); let {focusProps} = useFocus({ onBlur() { - // Set input value to normalized valid value - commit(); + commitAndAnnounce(); } }); @@ -187,27 +200,17 @@ export function useNumberField(props: AriaNumberFieldProps, state: NumberFieldSt let onPaste: ClipboardEventHandler = (e: ClipboardEvent) => { props.onPaste?.(e); let inputElement = e.target as HTMLInputElement; - if ( - inputElement && - ( - ((inputElement.selectionEnd ?? -1) - (inputElement.selectionStart ?? 0)) === inputElement.value.length - ) + // we can only handle the case where the paste takes over the entire input, otherwise things get very complicated + // trying to calculate the new string based on what the paste is replacing and where in the source string it is + if (inputElement && + ((inputElement.selectionEnd ?? -1) - (inputElement.selectionStart ?? 0)) === inputElement.value.length ) { e.preventDefault(); - let pastedText = e.clipboardData?.getData?.('text/plain')?.trim() ?? ''; - let isValid = state.validate(pastedText); - if (isValid) { - let value = state.parser.parse(pastedText); - let reformattedValue = numberFormatter.format(value); - if (state.validate(reformattedValue)) { - state.setInputValue(reformattedValue); - if (reformattedValue !== pastedText) { - announce(stringFormatter.format('pastedValue', {value: reformattedValue}), 'polite'); - } - } - } else { - announce(stringFormatter.format('couldNotParseValue', {value: pastedText}), 'polite'); - } + // commit so that the user gets to see what it formats to immediately + // paste happens before inputRef's value is updated, so have to prevent the default and do it ourselves + // spin button will then handle announcing the new value, this should work with controlled state as well + // because the announcement is done as a result of the new rendered input value if there is one + commit(e.clipboardData?.getData?.('text/plain')?.trim() ?? ''); } }; diff --git a/packages/@react-spectrum/numberfield/test/NumberField.test.js b/packages/@react-spectrum/numberfield/test/NumberField.test.js index 73f1c306fff..99749a7aeb9 100644 --- a/packages/@react-spectrum/numberfield/test/NumberField.test.js +++ b/packages/@react-spectrum/numberfield/test/NumberField.test.js @@ -925,17 +925,21 @@ describe('NumberField', function () { expect(announce).toHaveBeenCalledTimes(5); expect(announce).toHaveBeenLastCalledWith('$18.00', 'assertive'); act(() => {textField.blur();}); + expect(announce).toHaveBeenCalledTimes(6); + expect(announce).toHaveBeenLastCalledWith('$18.00', 'assertive'); expect(textField).toHaveAttribute('value', '$18.00'); expect(onChangeSpy).toHaveBeenCalledTimes(3); expect(onChangeSpy).toHaveBeenLastCalledWith(18); act(() => {textField.focus();}); await user.clear(textField); - expect(announce).toHaveBeenCalledTimes(6); + expect(announce).toHaveBeenCalledTimes(7); expect(announce).toHaveBeenLastCalledWith('Empty', 'assertive'); await user.keyboard('($32)'); expect(textField).toHaveAttribute('value', '($32)'); - expect(announce).toHaveBeenCalledTimes(9); + expect(announce).toHaveBeenNthCalledWith(8, '$3.00', 'assertive'); + expect(announce).toHaveBeenNthCalledWith(9, '$32.00', 'assertive'); + expect(announce).toHaveBeenCalledTimes(10); expect(announce).toHaveBeenLastCalledWith('−$32.00', 'assertive'); act(() => {textField.blur();}); expect(textField).toHaveAttribute('value', '($32.00)'); @@ -2314,7 +2318,7 @@ describe('NumberField', function () { it('resets to defaultValue when submitting form action', async () => { function Test() { const [value, formAction] = React.useActionState(() => 33, 22); - + return (
diff --git a/packages/@react-stately/numberfield/src/useNumberFieldState.ts b/packages/@react-stately/numberfield/src/useNumberFieldState.ts index 8abee9598b1..5bc374a4313 100644 --- a/packages/@react-stately/numberfield/src/useNumberFieldState.ts +++ b/packages/@react-stately/numberfield/src/useNumberFieldState.ts @@ -52,8 +52,9 @@ export interface NumberFieldState extends FormValidationState { * to the minimum and maximum values of the field, and snapped to the nearest step value. * This will fire the `onChange` prop with the new value, and if uncontrolled, update the `numberValue`. * Typically this is called when the field is blurred. + * @param value - The value to commit. If not provided, the current input value is used. */ - commit(): void, + commit(value?: string): void, /** Increments the current input value to the next step boundary, and fires `onChange`. */ increment(): void, /** Decrements the current input value to the next step boundary, and fires `onChange`. */ @@ -61,9 +62,7 @@ export interface NumberFieldState extends FormValidationState { /** Sets the current value to the `maxValue` if any, and fires `onChange`. */ incrementToMax(): void, /** Sets the current value to the `minValue` if any, and fires `onChange`. */ - decrementToMin(): void, - /** Attempts to parse a value in the current locale. */ - parser: NumberParser + decrementToMin(): void } export interface NumberFieldStateOptions extends NumberFieldProps { @@ -148,16 +147,21 @@ export function useNumberFieldState( } let parsedValue = useMemo(() => numberParser.parse(inputValue), [numberParser, inputValue]); - let commit = () => { + let commit = (overrideValue?: string) => { + let newInputValue = overrideValue === undefined ? inputValue : overrideValue; + let newParsedValue = parsedValue; + if (overrideValue !== undefined) { + newParsedValue = numberParser.parse(newInputValue); + } // Set to empty state if input value is empty - if (!inputValue.length) { + if (!newInputValue.length) { setNumberValue(NaN); setInputValue(value === undefined ? '' : format(numberValue)); return; } // if it failed to parse, then reset input to formatted version of current number - if (isNaN(parsedValue)) { + if (isNaN(newParsedValue)) { setInputValue(format(numberValue)); return; } @@ -165,9 +169,9 @@ export function useNumberFieldState( // Clamp to min and max, round to the nearest step, and round to specified number of digits let clampedValue: number; if (step === undefined || isNaN(step)) { - clampedValue = clamp(parsedValue, minValue, maxValue); + clampedValue = clamp(newParsedValue, minValue, maxValue); } else { - clampedValue = snapValueToStep(parsedValue, minValue, maxValue, step); + clampedValue = snapValueToStep(newParsedValue, minValue, maxValue, step); } clampedValue = numberParser.parse(format(clampedValue)); @@ -283,8 +287,7 @@ export function useNumberFieldState( setNumberValue, setInputValue, inputValue, - commit, - parser: numberParser + commit }; } diff --git a/packages/react-aria-components/test/NumberField.test.js b/packages/react-aria-components/test/NumberField.test.js index 05a494d2680..1d4686e532f 100644 --- a/packages/react-aria-components/test/NumberField.test.js +++ b/packages/react-aria-components/test/NumberField.test.js @@ -197,16 +197,6 @@ describe('NumberField', () => { expect(onChange).toHaveBeenCalledWith(1024); }); - it('should support pasting', async () => { - let {getByRole} = render(); - let input = getByRole('textbox'); - await user.tab(); - await user.clear(input); - await user.paste('1024'); - expect(input).toHaveValue('1,024'); - expect(announce).toHaveBeenCalledWith('Pasted value: 1,024', 'polite'); - }); - it('should support pasting into a format', async () => { let onChange = jest.fn(); let {getByRole} = render(); @@ -215,43 +205,19 @@ describe('NumberField', () => { await user.clear(input); await user.paste('1,024'); expect(input).toHaveValue('$1,024.00'); - expect(announce).toHaveBeenCalledWith('Pasted value: $1,024.00', 'polite'); - expect(onChange).not.toHaveBeenCalled(); - await user.keyboard('{Enter}'); + expect(announce).toHaveBeenCalledTimes(2); + expect(announce).toHaveBeenLastCalledWith('$1,024.00', 'assertive'); expect(onChange).toHaveBeenCalledWith(1024); }); - it('should tell users if their paste failed', async () => { - let {getByRole} = render(); - let input = getByRole('textbox'); - await user.tab(); - await user.clear(input); - await user.paste('$1.00 024 5.6'); - expect(input).toHaveValue(''); - expect(announce).toHaveBeenCalledWith('Could not understand value: $1.00 024 5.6, try another format perhaps', 'polite'); - }); - - it('should support paste announcements for a controlled numberfield', async () => { - function ControlledNumberField({value, ...props}) { - let [numberValue, setNumberValue] = useState(value); - return ; - } - let {getByRole} = render(); - let input = getByRole('textbox'); - await user.tab(); - await user.clear(input); - await user.paste('1024'); - expect(input).toHaveValue('1,024'); - expect(announce).toHaveBeenCalledWith('Pasted value: 1,024', 'polite'); - }); - it('should not change the input value if the new value is not accepted', async () => { let {getByRole} = render(); let input = getByRole('textbox'); await user.tab(); await user.clear(input); await user.paste('1024'); - expect(input).toHaveValue('1,024'); + expect(input).toHaveValue('200'); + expect(announce).toHaveBeenLastCalledWith('200', 'assertive'); await user.keyboard('{Enter}'); expect(input).toHaveValue('200'); }); From 5343c1ee8f1f5808200fbd5280c30b3e6bb9beb0 Mon Sep 17 00:00:00 2001 From: Robert Snow Date: Fri, 22 Aug 2025 17:07:39 +1000 Subject: [PATCH 8/8] fix lint --- packages/@react-aria/numberfield/src/useNumberField.ts | 2 +- packages/react-aria-components/test/NumberField.test.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@react-aria/numberfield/src/useNumberField.ts b/packages/@react-aria/numberfield/src/useNumberField.ts index 4d662dec154..353b9d68677 100644 --- a/packages/@react-aria/numberfield/src/useNumberField.ts +++ b/packages/@react-aria/numberfield/src/useNumberField.ts @@ -25,6 +25,7 @@ import { useState } from 'react'; import {DOMAttributes, GroupDOMAttributes, TextInputDOMProps, ValidationResult} from '@react-types/shared'; +import {flushSync} from 'react-dom'; // @ts-ignore import intlMessages from '../intl/*.json'; import {NumberFieldState} from '@react-stately/numberfield'; @@ -36,7 +37,6 @@ import { useNumberFormatter } from '@react-aria/i18n'; import {useSpinButton} from '@react-aria/spinbutton'; -import { flushSync } from 'react-dom'; export interface NumberFieldAria extends ValidationResult { /** Props for the label element. */ diff --git a/packages/react-aria-components/test/NumberField.test.js b/packages/react-aria-components/test/NumberField.test.js index 1d4686e532f..100220e4fff 100644 --- a/packages/react-aria-components/test/NumberField.test.js +++ b/packages/react-aria-components/test/NumberField.test.js @@ -14,7 +14,7 @@ jest.mock('@react-aria/live-announcer'); import {act, pointerMap, render} from '@react-spectrum/test-utils-internal'; import {announce} from '@react-aria/live-announcer'; import {Button, FieldError, Group, Input, Label, NumberField, NumberFieldContext, Text} from '../'; -import React, {useState} from 'react'; +import React from 'react'; import userEvent from '@testing-library/user-event'; let TestNumberField = (props) => (