@loadsmart/loadsmart-ui 5.16.1 → 5.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ export declare const getOrdinalSuffix: (number: number) => 'st' | 'nd' | 'rd' | 'th';
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loadsmart/loadsmart-ui",
3
- "version": "5.16.1",
3
+ "version": "5.18.0",
4
4
  "description": "Miranda UI, a React UI library",
5
5
  "main": "dist",
6
6
  "files": [
@@ -26,39 +26,6 @@
26
26
  "storybook-deployer": {
27
27
  "commitMessage": "chore(docs): deploy storybook to GitHub pages [skip ci]"
28
28
  },
29
- "jest": {
30
- "globals": {
31
- "window": true
32
- },
33
- "verbose": true,
34
- "coverageDirectory": "./coverage/",
35
- "collectCoverage": false,
36
- "setupFiles": [
37
- "./tests/jestsetup.tsx"
38
- ],
39
- "collectCoverageFrom": [
40
- "src/components/**/*.{js,tsx}",
41
- "!src/**/**/*.test.{js,tsx}"
42
- ],
43
- "coverageReporters": [
44
- "text",
45
- "lcov"
46
- ],
47
- "moduleDirectories": [
48
- "node_modules",
49
- "src"
50
- ],
51
- "moduleFileExtensions": [
52
- "ts",
53
- "tsx",
54
- "js",
55
- "json"
56
- ],
57
- "testMatch": [
58
- "**/__tests__/**/*.[jt]s?(x)",
59
- "**/?(*.)+(jest|test).[jt]s?(x)"
60
- ]
61
- },
62
29
  "lint-staged": {
63
30
  "src/**/*.{js,ts,jsx,tsx,json}": [
64
31
  "eslint --fix",
@@ -71,11 +38,10 @@
71
38
  ]
72
39
  },
73
40
  "devDependencies": {
74
- "@babel/cli": "7.13.16",
75
- "@babel/core": "7.14.0",
76
- "@babel/preset-env": "7.14.1",
77
- "@babel/preset-react": "7.13.13",
78
- "@babel/preset-typescript": "7.13.0",
41
+ "@babel/core": "^7.20.12",
42
+ "@babel/preset-env": "^7.20.2",
43
+ "@babel/preset-react": "^7.18.6",
44
+ "@babel/preset-typescript": "^7.18.6",
79
45
  "@commitlint/cli": "16.0.2",
80
46
  "@commitlint/config-conventional": "16.0.0",
81
47
  "@loadsmart/miranda-tokens": "1.3.0",
@@ -109,7 +75,7 @@
109
75
  "@testing-library/react-hooks": "^5.1.3",
110
76
  "@testing-library/user-event": "^13.5.0",
111
77
  "@types/chance": "1.1.1",
112
- "@types/jest": "26.0.23",
78
+ "@types/jest": "^27.5.2",
113
79
  "@types/lodash.debounce": "4.0.6",
114
80
  "@types/lodash.flatten": "4.4.6",
115
81
  "@types/lodash.get": "4.4.6",
@@ -126,7 +92,7 @@
126
92
  "@typescript-eslint/parser": "4.22.1",
127
93
  "@zerollup/ts-transform-paths": "1.7.18",
128
94
  "autoprefixer": "9",
129
- "babel-jest": "26.6.3",
95
+ "babel-jest": "^27.5.1",
130
96
  "babel-loader": "8.2.2",
131
97
  "babel-plugin-react-remove-properties": "0.3.0",
132
98
  "babel-plugin-styled-components": "1.12.0",
@@ -147,8 +113,8 @@
147
113
  "eslint-plugin-testing-library": "^5.9.1",
148
114
  "husky": "7.0.4",
149
115
  "identity-obj-proxy": "3.0.0",
150
- "jest": "26.6.3",
151
- "jest-svg-transformer": "1.0.0",
116
+ "jest": "^27.5.1",
117
+ "jest-transformer-svg": "1.0.2",
152
118
  "lint-staged": "11.0.0",
153
119
  "npm-run-all": "4.1.5",
154
120
  "postcss": "^7.0.39",
@@ -175,7 +141,7 @@
175
141
  "stylelint-no-unsupported-browser-features": "5.0.1",
176
142
  "stylelint-order": "4.1.0",
177
143
  "tailwindcss": "npm:@tailwindcss/postcss7-compat",
178
- "ts-jest": "26.5.6",
144
+ "ts-jest": "^27.1.5",
179
145
  "ts-node": "^10.9.1",
180
146
  "ts-toolbelt": "^9.6.0",
181
147
  "ttypescript": "^1.5.13",
@@ -134,5 +134,32 @@ describe('DateFormatHelper', () => {
134
134
  )
135
135
  ).toBe('02/01/2022 11:22:33')
136
136
  })
137
+
138
+ it('formats compound tokens', () => {
139
+ expect(
140
+ DateFormatHelper('ddd, Do, MMM').format(
141
+ DateHelper('2021-05-04T04:00:00Z', {
142
+ normalize: false,
143
+ })
144
+ )
145
+ ).toBe('Tue, 4th, May')
146
+ expect(
147
+ DateFormatHelper('ddd, DDo, MMM').format(
148
+ DateHelper('2021-05-05T04:00:00Z', {
149
+ normalize: false,
150
+ })
151
+ )
152
+ ).toBe('Wed, 05th, May')
153
+ })
154
+
155
+ it('does not formats a compound token when the previous token is invalid', () => {
156
+ expect(
157
+ DateFormatHelper('ddd, YYYYo, MMM').format(
158
+ DateHelper('2021-05-10T04:00:00Z', {
159
+ normalize: false,
160
+ })
161
+ )
162
+ ).toBe('Mon, 2021o, May')
163
+ })
137
164
  })
138
165
  })
@@ -2,11 +2,18 @@ import { identity } from '@loadsmart/utils-function'
2
2
  import { padded } from './Date.helper'
3
3
 
4
4
  import type { CalendarDate } from './Date.helper'
5
+ import { getOrdinalSuffix } from 'utils/toolset/getOrdinalSuffix'
5
6
 
6
7
  export interface DateFormat {
7
8
  format(date: CalendarDate): string
8
9
  }
9
10
 
11
+ const getTokenValue = (date: CalendarDate, token: string) => {
12
+ const value = DEFAULT_FORMATTERS[token].format(date.get())
13
+
14
+ return (ADDITIONAL_FORMATTERS[token] || identity)(value)
15
+ }
16
+
10
17
  /**
11
18
  * This helpers provides a convenient layer on top of `Intl.DateTimeFormat`,
12
19
  * using common tokens (based on `momentjs`) to format dates.
@@ -17,11 +24,16 @@ export default function DateFormatHelper(format: string): DateFormat {
17
24
  return {
18
25
  format(date: CalendarDate) {
19
26
  return tokens
20
- .map((token) => {
27
+ .map((token, index) => {
28
+ const previousIndex = index - 1
29
+ const previousToken = tokens[previousIndex]
30
+
21
31
  if (token in DEFAULT_FORMATTERS) {
22
- const value = DEFAULT_FORMATTERS[token].format(date.get())
32
+ return getTokenValue(date, token)
33
+ }
23
34
 
24
- return (ADDITIONAL_FORMATTERS[token] || identity)(value)
35
+ if (token in COMPOUND_FORMATTERS && COMPOUND_FORMATTERS[token].valid(previousToken)) {
36
+ return COMPOUND_FORMATTERS[token].format(getTokenValue(date, previousToken))
25
37
  }
26
38
 
27
39
  return token
@@ -39,16 +51,18 @@ export default function DateFormatHelper(format: string): DateFormat {
39
51
  *| Month | MM | 01, 02, ..., 11, 12 |
40
52
  *| | MMM | Jan, Feb, ..., Nov, Dec |
41
53
  *| | MMMM | January, February, ..., November,December |
42
- *| Day of Month | DD | 01, 02, ..., 30, 31 |
54
+ *| Day of Month | D | 1, 2, ..., 30, 31 |
55
+ *| Day of Month with leading 0 | DD | 01, 02, ..., 30, 31 |
43
56
  *| Day of week | ddd | Sun, Mon, ... Fri, Sat |
44
57
  *| | dddd | Sunday, Monday, ..., Friday, Saturday |
45
58
  *| Year | YYYY | 1970, 1971, ..., 2029, 2030 |
46
59
  *| Hour | HH | 00, 01, ..., 22, 23 |
47
60
  *| | hh | 00, 01, ..., 11, 12 |
48
- *| Minute | mm | 01, 02, ..., 11, 12 |
61
+ *| Minute | mm | 01, 02, ..., 58, 59 |
49
62
  *| Seconds | ss | 01, 02, ..., 58, 59 |
50
63
  *| Post or ante meridiem | a | am, pm |
51
64
  *| | A | AM, PM |
65
+ *| Ordinal numbers | o | 1st, 2nd, 3rd, ..., 10th |
52
66
  *| Scaped sequence | [] | |
53
67
  *
54
68
  * @param format
@@ -56,7 +70,10 @@ export default function DateFormatHelper(format: string): DateFormat {
56
70
  */
57
71
  export function tokenizer(format: string): string[] {
58
72
  function getType(char?: string): string {
59
- if (char != undefined && ['M', 'd', 'D', 'Y', 'H', 'h', 'm', 's', 'A', 'a'].includes(char)) {
73
+ if (
74
+ char != undefined &&
75
+ ['M', 'd', 'D', 'Y', 'H', 'h', 'm', 's', 'A', 'a', 'o'].includes(char)
76
+ ) {
60
77
  return 'token'
61
78
  }
62
79
 
@@ -110,6 +127,9 @@ const DEFAULT_FORMATTERS: Record<string, Intl.DateTimeFormat> = {
110
127
  MMMM: new Intl.DateTimeFormat('en-US', {
111
128
  month: 'long',
112
129
  }),
130
+ D: new Intl.DateTimeFormat('en-US', {
131
+ day: 'numeric',
132
+ }),
113
133
  DD: new Intl.DateTimeFormat('en-US', {
114
134
  day: '2-digit',
115
135
  }),
@@ -154,10 +174,22 @@ const DEFAULT_FORMATTERS: Record<string, Intl.DateTimeFormat> = {
154
174
  * Padding, for example, is applied in some cases due to [this](https://bugs.chromium.org/p/chromium/issues/detail?id=527926) bug.
155
175
  */
156
176
  const ADDITIONAL_FORMATTERS: Record<string, (value: string) => string> = {
157
- hh: (value: string) => (value ? padded(value.split(' ')[0], 2) : value),
177
+ hh: (value: string) => (value ? padded(value.split(/\s/)[0], 2) : value),
158
178
  HH: (value: string) => (value ? padded(value, 2) : value),
159
179
  mm: (value: string) => (value ? padded(value, 2) : value),
160
180
  ss: (value: string) => (value ? padded(value, 2) : value),
161
- a: (value: string) => (value ? (value.split(' ')[1] || '').toLowerCase() : value),
162
- A: (value: string) => (value ? (value.split(' ')[1] || '').toUpperCase() : value),
181
+ a: (value: string) => (value ? (value.split(/\s/)[1] || '').toLowerCase() : value),
182
+ A: (value: string) => (value ? (value.split(/\s/)[1] || '').toUpperCase() : value),
183
+ }
184
+
185
+ type CompoundFormatter = {
186
+ valid: (token: string) => boolean
187
+ format: (value: string) => string
188
+ }
189
+
190
+ const COMPOUND_FORMATTERS: Record<string, CompoundFormatter> = {
191
+ o: {
192
+ valid: (token) => new Set(['D', 'DD']).has(token),
193
+ format: (value: string) => getOrdinalSuffix(parseInt(value, 10)),
194
+ },
163
195
  }
@@ -51,9 +51,30 @@ describe('<Label />', () => {
51
51
  setup(props)
52
52
 
53
53
  const tipAnchor = screen.getByText('?', { selector: 'span' })
54
- expect(() => screen.getByText(/`${props.message}`/i)).toThrow()
54
+ expect(screen.queryByText(/`${props.message}`/i)).not.toBeInTheDocument()
55
55
 
56
56
  userEvent.hover(tipAnchor)
57
57
  screen.getByText(props.tip)
58
58
  })
59
+
60
+ it('renders node tip', () => {
61
+ const sentence = generator.sentence()
62
+ const props = {
63
+ children: generator.word(),
64
+ tip: (
65
+ <div data-testid="tip-div-id">
66
+ <b>{sentence}</b>
67
+ </div>
68
+ ),
69
+ }
70
+
71
+ setup(props)
72
+
73
+ const tipAnchor = screen.getByText('?', { selector: 'span' })
74
+ expect(screen.queryByText(/`${props.message}`/i)).not.toBeInTheDocument()
75
+
76
+ userEvent.hover(tipAnchor)
77
+ expect(screen.getByTestId('tip-div-id')).toBeInTheDocument()
78
+ screen.getByText(sentence)
79
+ })
59
80
  })
@@ -1,4 +1,4 @@
1
- import React from 'react'
1
+ import React, { ReactNode } from 'react'
2
2
  import type { LabelHTMLAttributes } from 'react'
3
3
  import styled from 'styled-components'
4
4
  import clsx from 'clsx'
@@ -20,7 +20,7 @@ interface WithAdditionalProps {
20
20
  disabled?: boolean
21
21
  required?: boolean
22
22
  scheme?: ColorScheme
23
- tip?: string
23
+ tip?: ReactNode
24
24
  }
25
25
 
26
26
  export interface LabelProps
@@ -41,14 +41,6 @@ function Experiment(overrides: Partial<SelectProps>) {
41
41
  }
42
42
 
43
43
  describe('SelectEvent', () => {
44
- beforeEach(() => {
45
- jest.useFakeTimers()
46
- })
47
- afterEach(() => {
48
- jest.runOnlyPendingTimers()
49
- jest.useRealTimers()
50
- })
51
-
52
44
  const setup = (overrides: Partial<SelectProps>) =>
53
45
  renderer(<Experiment {...overrides} />).render()
54
46
 
@@ -0,0 +1,27 @@
1
+ import { getOrdinalSuffix } from './getOrdinalSuffix'
2
+
3
+ it('should return "st" for numbers ending with 1', () => {
4
+ expect(getOrdinalSuffix(1)).toBe('st')
5
+ expect(getOrdinalSuffix(101)).toBe('st')
6
+ expect(getOrdinalSuffix(1001)).toBe('st')
7
+ })
8
+
9
+ it('should return "nd" for numbers ending with 2', () => {
10
+ expect(getOrdinalSuffix(2)).toBe('nd')
11
+ expect(getOrdinalSuffix(102)).toBe('nd')
12
+ expect(getOrdinalSuffix(1002)).toBe('nd')
13
+ })
14
+
15
+ it('should return "rd" for numbers ending with 3', () => {
16
+ expect(getOrdinalSuffix(3)).toBe('rd')
17
+ expect(getOrdinalSuffix(103)).toBe('rd')
18
+ expect(getOrdinalSuffix(1003)).toBe('rd')
19
+ })
20
+
21
+ it('should return "th" for 0 and numbers higher than 3 but not ending with 1, 2 or 3', () => {
22
+ expect(getOrdinalSuffix(0)).toBe('th')
23
+ expect(getOrdinalSuffix(4)).toBe('th')
24
+ expect(getOrdinalSuffix(10)).toBe('th')
25
+ expect(getOrdinalSuffix(100)).toBe('th')
26
+ expect(getOrdinalSuffix(1000)).toBe('th')
27
+ })
@@ -0,0 +1,15 @@
1
+ export const getOrdinalSuffix = (number: number): 'st' | 'nd' | 'rd' | 'th' => {
2
+ if (number % 10 === 1 && number !== 11) {
3
+ return 'st'
4
+ }
5
+
6
+ if (number % 10 === 2 && number !== 12) {
7
+ return 'nd'
8
+ }
9
+
10
+ if (number % 10 === 3 && number !== 13) {
11
+ return 'rd'
12
+ }
13
+
14
+ return 'th'
15
+ }