react-intl 6.0.3 → 6.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/BUILD +130 -0
  2. package/CHANGELOG.md +1384 -0
  3. package/LICENSE.md +0 -0
  4. package/README.md +0 -0
  5. package/example-sandboxes/rescripts/.rescriptsrc.js +28 -0
  6. package/example-sandboxes/rescripts/package-lock.json +18035 -0
  7. package/example-sandboxes/rescripts/package.json +35 -0
  8. package/example-sandboxes/rescripts/public/index.html +42 -0
  9. package/example-sandboxes/rescripts/src/App.tsx +16 -0
  10. package/example-sandboxes/rescripts/src/index.tsx +5 -0
  11. package/example-sandboxes/rescripts/src/react-app-env.d.ts +1 -0
  12. package/example-sandboxes/rescripts/src/styles.css +4 -0
  13. package/example-sandboxes/rescripts/tsconfig.json +20 -0
  14. package/example-sandboxes/strict-locale-type/.env +1 -0
  15. package/example-sandboxes/strict-locale-type/package-lock.json +14595 -0
  16. package/example-sandboxes/strict-locale-type/package.json +35 -0
  17. package/example-sandboxes/strict-locale-type/src/App.tsx +48 -0
  18. package/example-sandboxes/strict-locale-type/src/index.html +28 -0
  19. package/example-sandboxes/strict-locale-type/src/index.tsx +7 -0
  20. package/example-sandboxes/strict-locale-type/src/styles.css +4 -0
  21. package/example-sandboxes/strict-locale-type/tsconfig.json +7 -0
  22. package/example-sandboxes/strict-message-types/.env +1 -0
  23. package/example-sandboxes/strict-message-types/package-lock.json +14596 -0
  24. package/example-sandboxes/strict-message-types/package.json +35 -0
  25. package/example-sandboxes/strict-message-types/src/App.tsx +31 -0
  26. package/example-sandboxes/strict-message-types/src/index.html +28 -0
  27. package/example-sandboxes/strict-message-types/src/index.tsx +7 -0
  28. package/example-sandboxes/strict-message-types/src/styles.css +4 -0
  29. package/example-sandboxes/strict-message-types/tsconfig.json +7 -0
  30. package/examples/BUILD +70 -0
  31. package/examples/Bug2727.tsx +37 -0
  32. package/examples/HandleChange.tsx +48 -0
  33. package/examples/Hooks.tsx +126 -0
  34. package/examples/Injected.tsx +41 -0
  35. package/examples/Messages.tsx +82 -0
  36. package/examples/StaticTypeSafetyAndCodeSplitting/StaticTypeSafetyAndCodeSplitting.tsx +44 -0
  37. package/examples/StaticTypeSafetyAndCodeSplitting/en.json +3 -0
  38. package/examples/StaticTypeSafetyAndCodeSplitting/intlHelpers.tsx +39 -0
  39. package/examples/StaticTypeSafetyAndCodeSplitting/it.json +3 -0
  40. package/examples/TimeZone.tsx +44 -0
  41. package/examples/advanced/Advanced.tsx +68 -0
  42. package/examples/advanced/compiled-lang/en.json +77 -0
  43. package/examples/advanced/compiled-lang/fr.json +77 -0
  44. package/examples/index.html +20 -0
  45. package/examples/index.tsx +44 -0
  46. package/examples/package.json +20 -0
  47. package/index.ts +127 -0
  48. package/jest.config.js +27 -0
  49. package/package.json +12 -8
  50. package/src/components/createFormattedComponent.tsx +123 -0
  51. package/src/components/dateTimeRange.tsx +26 -0
  52. package/src/components/injectIntl.tsx +111 -0
  53. package/src/components/message.tsx +73 -0
  54. package/src/components/plural.tsx +45 -0
  55. package/src/components/provider.tsx +196 -0
  56. package/src/components/relative.tsx +192 -0
  57. package/src/components/useIntl.ts +10 -0
  58. package/src/types.ts +29 -0
  59. package/src/utils.ts +77 -0
  60. package/tests/functional/index.ts +18 -0
  61. package/tests/functional/support/build.ts +16 -0
  62. package/tests/functional/support/format.tsx +112 -0
  63. package/tests/perf/index.tsx +196 -0
  64. package/tests/setup.js +10 -0
  65. package/tests/unit/components/__snapshots__/displayName.tsx.snap +19 -0
  66. package/tests/unit/components/__snapshots__/message.tsx.snap +41 -0
  67. package/tests/unit/components/__snapshots__/relative.tsx.snap +11 -0
  68. package/tests/unit/components/__snapshots__/useIntl.tsx.snap +25 -0
  69. package/tests/unit/components/date.tsx +233 -0
  70. package/tests/unit/components/dateTimeRange.tsx +103 -0
  71. package/tests/unit/components/displayName.tsx +57 -0
  72. package/tests/unit/components/message.tsx +509 -0
  73. package/tests/unit/components/number.tsx +198 -0
  74. package/tests/unit/components/plural.tsx +116 -0
  75. package/tests/unit/components/provider.tsx +215 -0
  76. package/tests/unit/components/relative.tsx +263 -0
  77. package/tests/unit/components/time.tsx +242 -0
  78. package/tests/unit/components/useIntl.tsx +64 -0
  79. package/tests/unit/components/withIntl.tsx +66 -0
  80. package/tests/unit/react-intl.ts +88 -0
  81. package/tests/unit/testUtils.tsx +42 -0
  82. package/tsconfig.json +5 -0
  83. package/index.d.ts +0 -43
  84. package/index.d.ts.map +0 -1
  85. package/index.js +0 -51
  86. package/lib/index.d.ts +0 -43
  87. package/lib/index.d.ts.map +0 -1
  88. package/lib/index.js +0 -26
  89. package/lib/src/components/createFormattedComponent.d.ts +0 -29
  90. package/lib/src/components/createFormattedComponent.d.ts.map +0 -1
  91. package/lib/src/components/createFormattedComponent.js +0 -62
  92. package/lib/src/components/dateTimeRange.d.ts +0 -11
  93. package/lib/src/components/dateTimeRange.d.ts.map +0 -1
  94. package/lib/src/components/dateTimeRange.js +0 -15
  95. package/lib/src/components/injectIntl.d.ts +0 -22
  96. package/lib/src/components/injectIntl.d.ts.map +0 -1
  97. package/lib/src/components/injectIntl.js +0 -29
  98. package/lib/src/components/message.d.ts +0 -12
  99. package/lib/src/components/message.d.ts.map +0 -1
  100. package/lib/src/components/message.js +0 -35
  101. package/lib/src/components/plural.d.ts +0 -15
  102. package/lib/src/components/plural.d.ts.map +0 -1
  103. package/lib/src/components/plural.js +0 -26
  104. package/lib/src/components/provider.d.ts +0 -35
  105. package/lib/src/components/provider.d.ts.map +0 -1
  106. package/lib/src/components/provider.js +0 -108
  107. package/lib/src/components/relative.d.ts +0 -12
  108. package/lib/src/components/relative.d.ts.map +0 -1
  109. package/lib/src/components/relative.js +0 -129
  110. package/lib/src/components/useIntl.d.ts +0 -3
  111. package/lib/src/components/useIntl.d.ts.map +0 -1
  112. package/lib/src/components/useIntl.js +0 -8
  113. package/lib/src/types.d.ts +0 -12
  114. package/lib/src/types.d.ts.map +0 -1
  115. package/lib/src/types.js +0 -1
  116. package/lib/src/utils.d.ts +0 -14
  117. package/lib/src/utils.d.ts.map +0 -1
  118. package/lib/src/utils.js +0 -43
  119. package/react-intl.iife.js +0 -7485
  120. package/src/components/createFormattedComponent.d.ts +0 -29
  121. package/src/components/createFormattedComponent.d.ts.map +0 -1
  122. package/src/components/createFormattedComponent.js +0 -69
  123. package/src/components/dateTimeRange.d.ts +0 -11
  124. package/src/components/dateTimeRange.d.ts.map +0 -1
  125. package/src/components/dateTimeRange.js +0 -17
  126. package/src/components/injectIntl.d.ts +0 -22
  127. package/src/components/injectIntl.d.ts.map +0 -1
  128. package/src/components/injectIntl.js +0 -33
  129. package/src/components/message.d.ts +0 -12
  130. package/src/components/message.d.ts.map +0 -1
  131. package/src/components/message.js +0 -37
  132. package/src/components/plural.d.ts +0 -15
  133. package/src/components/plural.d.ts.map +0 -1
  134. package/src/components/plural.js +0 -29
  135. package/src/components/provider.d.ts +0 -35
  136. package/src/components/provider.d.ts.map +0 -1
  137. package/src/components/provider.js +0 -112
  138. package/src/components/relative.d.ts +0 -12
  139. package/src/components/relative.d.ts.map +0 -1
  140. package/src/components/relative.js +0 -131
  141. package/src/components/useIntl.d.ts +0 -3
  142. package/src/components/useIntl.d.ts.map +0 -1
  143. package/src/components/useIntl.js +0 -12
  144. package/src/types.d.ts +0 -12
  145. package/src/types.d.ts.map +0 -1
  146. package/src/types.js +0 -2
  147. package/src/utils.d.ts +0 -14
  148. package/src/utils.d.ts.map +0 -1
  149. package/src/utils.js +0 -49
@@ -0,0 +1,233 @@
1
+ import * as React from 'react'
2
+ import {FormattedDate, FormattedDateParts} from '../../../'
3
+ import {mountFormattedComponentWithProvider} from '../testUtils'
4
+ import {createIntl} from '../../../src/components/provider'
5
+ import {IntlShape} from '../../../'
6
+ import {render} from '@testing-library/react'
7
+
8
+ const mountWithProvider = mountFormattedComponentWithProvider(FormattedDate)
9
+ const mountPartsWithProvider =
10
+ mountFormattedComponentWithProvider(FormattedDateParts)
11
+
12
+ describe('<FormattedDate>', () => {
13
+ let intl: IntlShape
14
+ beforeEach(() => {
15
+ intl = createIntl({
16
+ locale: 'en',
17
+ onError: () => {},
18
+ })
19
+ })
20
+
21
+ it('has a `displayName`', () => {
22
+ expect(typeof FormattedDate.displayName).toBe('string')
23
+ })
24
+
25
+ it('throws when <IntlProvider> is missing from ancestry', () => {
26
+ // So it doesn't spam the console
27
+ jest.spyOn(console, 'error').mockImplementation(() => {})
28
+ expect(() => render(<FormattedDate value={Date.now()} />)).toThrow(Error)
29
+ })
30
+
31
+ it('requires a finite `value` prop', () => {
32
+ const value = Date.now()
33
+ const onError = jest.fn()
34
+ mountWithProvider({value}, {...intl, onError})
35
+ expect(isFinite(value)).toBe(true)
36
+ expect(onError).toHaveBeenCalledTimes(0)
37
+
38
+ mountWithProvider({value: NaN}, {...intl, onError})
39
+ expect(onError).toHaveBeenCalledTimes(1)
40
+ expect(onError.mock.calls[0][0].code).toBe('FORMAT_ERROR')
41
+ })
42
+
43
+ it('renders a formatted date in a <>', () => {
44
+ const date = Date.now()
45
+
46
+ const {getByTestId} = mountWithProvider({value: date}, intl)
47
+
48
+ expect(getByTestId('comp')).toHaveTextContent(intl.formatDate(date))
49
+ })
50
+ it('renders a formatted date w/o textComponent', () => {
51
+ const date = Date.now()
52
+
53
+ const {getByTestId} = mountWithProvider(
54
+ {value: date},
55
+ {...intl, textComponent: '' as any}
56
+ )
57
+
58
+ expect(getByTestId('comp')).toHaveTextContent(intl.formatDate(date))
59
+ })
60
+
61
+ it('accepts valid Intl.DateTimeFormat options as props', () => {
62
+ const date = new Date()
63
+ const options: Intl.DateTimeFormatOptions = {year: 'numeric'}
64
+
65
+ const {getByTestId} = mountWithProvider({value: date, ...options}, intl)
66
+
67
+ expect(getByTestId('comp')).toHaveTextContent(
68
+ intl.formatDate(date, options)
69
+ )
70
+ })
71
+
72
+ it('falls back and warns on invalid Intl.DateTimeFormat options', () => {
73
+ const date = new Date()
74
+ const onError = jest.fn()
75
+ const {getByTestId} = mountWithProvider(
76
+ // @ts-expect-error invalid value for testing
77
+ {value: date, year: 'invalid'},
78
+ {...intl, onError}
79
+ )
80
+
81
+ expect(getByTestId('comp')).toHaveTextContent(String(date))
82
+ expect(onError).toHaveBeenCalled()
83
+ expect(onError.mock.calls[0][0].code).toBe('FORMAT_ERROR')
84
+ })
85
+
86
+ it('accepts `format` prop', () => {
87
+ intl = createIntl({
88
+ onError: () => {},
89
+ locale: 'en',
90
+ formats: {
91
+ date: {
92
+ 'year-only': {year: 'numeric'},
93
+ },
94
+ },
95
+ })
96
+
97
+ const date = new Date()
98
+ const format = 'year-only'
99
+
100
+ const {getByTestId} = mountWithProvider({value: date, format}, intl)
101
+
102
+ expect(getByTestId('comp')).toHaveTextContent(
103
+ intl.formatDate(date, {format})
104
+ )
105
+ })
106
+
107
+ it('supports function-as-child pattern', () => {
108
+ const date = Date.now()
109
+
110
+ const spyChildren = jest
111
+ .fn()
112
+ .mockImplementation(() => <b data-testid="b">Jest</b>)
113
+ const {getByTestId} = mountWithProvider(
114
+ {
115
+ value: date,
116
+ children: spyChildren,
117
+ },
118
+ intl
119
+ )
120
+
121
+ expect(spyChildren).toHaveBeenCalledTimes(1)
122
+ expect(spyChildren.mock.calls[0]).toEqual([intl.formatDate(date)])
123
+
124
+ expect(getByTestId('b')).toHaveTextContent('Jest')
125
+ expect(getByTestId('b').tagName).toBe('B')
126
+ })
127
+ })
128
+
129
+ describe('<FormattedDateParts>', () => {
130
+ let intl: IntlShape
131
+ const children = jest.fn(
132
+ parts => (Array.isArray(parts) && parts[0] && parts[0].value) || null
133
+ )
134
+
135
+ beforeEach(() => {
136
+ intl = createIntl({
137
+ locale: 'en',
138
+ onError: () => {},
139
+ })
140
+ children.mockClear()
141
+ })
142
+
143
+ it('has a `displayName`', () => {
144
+ expect(FormattedDateParts.displayName).toBe('FormattedDateParts')
145
+ })
146
+
147
+ it('throws when <IntlProvider> is missing from ancestry', () => {
148
+ // So it doesn't spam the console
149
+ jest.spyOn(console, 'error').mockImplementation(() => {})
150
+ expect(() =>
151
+ render(<FormattedDateParts children={children} value={Date.now()} />)
152
+ ).toThrow(
153
+ '[React Intl] Could not find required `intl` object. <IntlProvider> needs to exist in the component ancestry.'
154
+ )
155
+ })
156
+
157
+ it('requires a finite `value` prop', () => {
158
+ const value = Date.now()
159
+ const onError = jest.fn()
160
+ mountPartsWithProvider({value, children}, {...intl, onError})
161
+ expect(isFinite(value)).toBe(true)
162
+ expect(onError).toHaveBeenCalledTimes(0)
163
+
164
+ mountPartsWithProvider({value: NaN, children}, {...intl, onError})
165
+ expect(onError).toHaveBeenCalledTimes(1)
166
+ expect(onError.mock.calls[0][0].code).toBe('FORMAT_ERROR')
167
+ })
168
+
169
+ it('accepts valid Intl.DateTimeFormat options as props', () => {
170
+ const date = new Date(1567130870626)
171
+ const options: Intl.DateTimeFormatOptions = {year: 'numeric'}
172
+
173
+ mountPartsWithProvider({value: date, children, ...options}, intl)
174
+
175
+ expect(children.mock.calls[0][0]).toEqual(
176
+ intl.formatDateToParts(date, options)
177
+ )
178
+ })
179
+
180
+ it('falls back and warns on invalid Intl.DateTimeFormat options', () => {
181
+ const date = new Date(1567130870626)
182
+ const onError = jest.fn()
183
+ mountPartsWithProvider(
184
+ // @ts-expect-error invalid value for testing
185
+ {value: date, year: 'invalid', children},
186
+ {...intl, onError}
187
+ )
188
+
189
+ expect(children.mock.calls[0][0]).toEqual(
190
+ // @ts-expect-error invalid value for testing
191
+ intl.formatDateToParts(date, {year: 'invalid'})
192
+ )
193
+ expect(onError).toHaveBeenCalled()
194
+ expect(onError.mock.calls[0][0].code).toBe('FORMAT_ERROR')
195
+ })
196
+
197
+ it('renders a string date', () => {
198
+ const date = new Date()
199
+
200
+ mountPartsWithProvider({value: date + '', children}, intl)
201
+
202
+ expect(children.mock.calls[0][0]).toEqual(intl.formatDateToParts(date))
203
+ })
204
+
205
+ it('renders date 0 if value is ""', () => {
206
+ const date = new Date(0)
207
+
208
+ mountPartsWithProvider({value: '', children}, intl)
209
+
210
+ expect(children.mock.calls[0][0]).toEqual(intl.formatDateToParts(date))
211
+ })
212
+
213
+ it('accepts `format` prop', () => {
214
+ intl = createIntl({
215
+ onError: () => {},
216
+ locale: 'en',
217
+ formats: {
218
+ date: {
219
+ 'year-only': {year: 'numeric'},
220
+ },
221
+ },
222
+ })
223
+
224
+ const date = new Date(1567130870626)
225
+ const format = 'year-only'
226
+
227
+ mountPartsWithProvider({value: date, format, children}, intl)
228
+
229
+ expect(children.mock.calls[0][0]).toEqual(
230
+ intl.formatDateToParts(date, {format})
231
+ )
232
+ })
233
+ })
@@ -0,0 +1,103 @@
1
+ import * as React from 'react'
2
+ import {FormattedDateTimeRange} from '../../../'
3
+ import {mountFormattedComponentWithProvider} from '../testUtils'
4
+ import {createIntl} from '../../../src/components/provider'
5
+ import {IntlShape} from '../../../'
6
+ import {render} from '@testing-library/react'
7
+ const mountWithProvider = mountFormattedComponentWithProvider(
8
+ FormattedDateTimeRange
9
+ )
10
+
11
+ describe('<FormattedDateTimeRange>', () => {
12
+ let intl: IntlShape
13
+ beforeEach(() => {
14
+ intl = createIntl({
15
+ onError: () => {},
16
+ locale: 'en',
17
+ })
18
+ })
19
+
20
+ it('has a `displayName`', () => {
21
+ expect(typeof FormattedDateTimeRange.displayName).toBe('string')
22
+ })
23
+
24
+ it('throws when <IntlProvider> is missing from ancestry', () => {
25
+ // So it doesn't spam the console
26
+ jest.spyOn(console, 'error').mockImplementation(() => {})
27
+ expect(() =>
28
+ render(<FormattedDateTimeRange from={Date.now()} to={Date.now()} />)
29
+ ).toThrow(Error)
30
+ })
31
+
32
+ it('renders a formatted date in a <>', () => {
33
+ const from = new Date('2020-1-1')
34
+ const to = new Date('2020-1-15')
35
+
36
+ const {getByTestId} = mountWithProvider({from, to}, intl)
37
+
38
+ expect(getByTestId('comp')).toHaveTextContent(
39
+ intl.formatDateTimeRange(from, to)
40
+ )
41
+ })
42
+ it('renders a formatted date w/o textComponent', () => {
43
+ const from = new Date('2020-1-1')
44
+ const to = new Date('2020-1-15')
45
+ const {getByTestId} = mountWithProvider(
46
+ {from, to},
47
+ {...intl, textComponent: '' as any}
48
+ )
49
+
50
+ expect(getByTestId('comp')).toHaveTextContent('1/1/2020 – 1/15/2020')
51
+ })
52
+
53
+ it('accepts valid Intl.DateTimeFormat options as props', () => {
54
+ const from = new Date('2020-1-1')
55
+ const to = new Date('2020-1-15')
56
+ const options: Intl.DateTimeFormatOptions = {year: 'numeric'}
57
+
58
+ const {getByTestId} = mountWithProvider({from, to, ...options}, intl)
59
+
60
+ expect(getByTestId('comp')).toHaveTextContent(
61
+ intl.formatDateTimeRange(from, to, options)
62
+ )
63
+ })
64
+
65
+ it('falls back and warns on invalid Intl.DateTimeFormat options', () => {
66
+ const from = new Date()
67
+ const onError = jest.fn()
68
+ const {getByTestId} = mountWithProvider(
69
+ // @ts-expect-error invalid for testing
70
+ {from, to: undefined, year: 'invalid'},
71
+ {...intl, onError}
72
+ )
73
+
74
+ expect(getByTestId('comp')).toHaveTextContent(String(from))
75
+ expect(onError).toHaveBeenCalled()
76
+ expect(onError.mock.calls[0][0].code).toBe('FORMAT_ERROR')
77
+ })
78
+
79
+ it('supports function-as-child pattern', () => {
80
+ const from = new Date('2020-1-1')
81
+ const to = new Date('2020-1-15')
82
+ const spyChildren = jest
83
+ .fn()
84
+ .mockImplementation(() => <b data-testid="b">Jest</b>)
85
+ const {getByTestId} = mountWithProvider(
86
+ {
87
+ from,
88
+ to,
89
+ children: spyChildren,
90
+ },
91
+ intl
92
+ )
93
+
94
+ expect(spyChildren).toHaveBeenCalledTimes(1)
95
+ expect(spyChildren.mock.calls[0]).toEqual([
96
+ intl.formatDateTimeRange(from, to),
97
+ ])
98
+
99
+ const rendered = getByTestId('b')
100
+ expect(rendered.tagName).toBe('B')
101
+ expect(rendered).toHaveTextContent('Jest')
102
+ })
103
+ })
@@ -0,0 +1,57 @@
1
+ import * as React from 'react'
2
+
3
+ import {FormattedDisplayName} from '../../../'
4
+ import {mountFormattedComponentWithProvider} from '../testUtils'
5
+ import {render} from '@testing-library/react'
6
+ const mountWithProvider =
7
+ mountFormattedComponentWithProvider(FormattedDisplayName)
8
+
9
+ const intlConfig = {locale: 'en'}
10
+
11
+ describe('<FormattedDisplayName />', () => {
12
+ it('has a `displayName`', () => {
13
+ expect(FormattedDisplayName.displayName).toBe('FormattedDisplayName')
14
+ })
15
+
16
+ it('throws when <IntlProvider> is missing from ancestry', () => {
17
+ // So it doesn't spam the console
18
+ jest.spyOn(console, 'error').mockImplementation(() => {})
19
+ expect(() =>
20
+ render(<FormattedDisplayName type="language" value="zh-Hans" />)
21
+ ).toThrow(
22
+ '[React Intl] Could not find required `intl` object. <IntlProvider> needs to exist in the component ancestry.'
23
+ )
24
+ })
25
+
26
+ it('accepts Intl.DisplayNames options', () => {
27
+ const {container} = mountWithProvider(
28
+ {
29
+ type: 'currency',
30
+ value: 'CNY',
31
+ },
32
+ intlConfig
33
+ )
34
+ expect(container).toMatchSnapshot()
35
+ })
36
+
37
+ it('renders an empty <> when the underlying DisplayNames would return undefined', () => {
38
+ // When fallback is none, it will return undefined if no display name is available.
39
+ const displayNames = new (Intl as any).DisplayNames('en', {
40
+ type: 'language',
41
+ fallback: 'none',
42
+ })
43
+ expect(displayNames.of('xx-XX')).toBeUndefined()
44
+
45
+ // Now let's do the same with <FormattedDisplayNames />
46
+ const {container} = mountWithProvider(
47
+ {
48
+ type: 'language',
49
+ fallback: 'none',
50
+ value: 'xx-XX',
51
+ },
52
+ intlConfig
53
+ )
54
+
55
+ expect(container).toMatchSnapshot()
56
+ })
57
+ })