superdesk-ui-framework 5.0.2 → 5.0.4

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 (48) hide show
  1. package/.mocharc.json +1 -1
  2. package/app/scripts/helpers/dropdown.helper.js +9 -0
  3. package/app/styles/_buttons.scss +4 -1
  4. package/app/styles/_helpers.scss +25 -6
  5. package/app/styles/_popover.scss +8 -8
  6. package/app/styles/_tooltips.scss +83 -0
  7. package/app/styles/design-tokens/_design-tokens-general.scss +6 -0
  8. package/app/styles/form-elements/_checkbox.scss +9 -5
  9. package/app/styles/form-elements/_inputs.scss +7 -1
  10. package/app-typescript/components/Autocomplete.tsx +1 -1
  11. package/app-typescript/components/Form/InputWrapper.tsx +1 -1
  12. package/app-typescript/components/Popover.tsx +2 -0
  13. package/app-typescript/components/SelectGrid.tsx +3 -1
  14. package/app-typescript/components/TimePicker/TimePickerPopover.spec.ts +163 -0
  15. package/app-typescript/components/TimePicker/TimePickerPopover.tsx +286 -0
  16. package/app-typescript/components/TimePicker/TimeValueHolder.tsx +36 -0
  17. package/app-typescript/components/{TimePicker.tsx → TimePicker/index.tsx} +6 -12
  18. package/app-typescript/components/Tooltip.tsx +1 -1
  19. package/app-typescript/components/TooltipV2.tsx +10 -21
  20. package/app-typescript/components/_Positioner.tsx +2 -0
  21. package/dist/components/Popover.tsx +31 -8
  22. package/dist/components/TimePicker.tsx +7 -2
  23. package/dist/components/Tooltips.tsx +15 -13
  24. package/dist/components/utilities/SpacingUtilities.tsx +126 -126
  25. package/dist/components/utilities/TextUtilities.tsx +51 -37
  26. package/dist/examples.bundle.js +1354 -1199
  27. package/dist/superdesk-ui.bundle.css +146 -41
  28. package/dist/superdesk-ui.bundle.js +829 -741
  29. package/dist/vendor.bundle.js +15 -15
  30. package/package.json +1 -1
  31. package/react/components/Autocomplete.js +2 -1
  32. package/react/components/Form/InputWrapper.js +2 -1
  33. package/react/components/Popover.d.ts +1 -0
  34. package/react/components/Popover.js +1 -1
  35. package/react/components/SelectGrid.js +2 -1
  36. package/react/components/TimePicker/TimePickerPopover.d.ts +27 -0
  37. package/react/components/TimePicker/TimePickerPopover.js +231 -0
  38. package/react/components/TimePicker/TimeValueHolder.d.ts +13 -0
  39. package/react/components/TimePicker/TimeValueHolder.js +73 -0
  40. package/react/components/{TimePicker.d.ts → TimePicker/index.d.ts} +1 -1
  41. package/react/components/{TimePicker.js → TimePicker/index.js} +5 -9
  42. package/react/components/Tooltip.js +1 -1
  43. package/react/components/TooltipV2.js +10 -18
  44. package/react/components/_Positioner.d.ts +1 -0
  45. package/react/components/_Positioner.js +1 -1
  46. package/app-typescript/components/TimePickerPopover.tsx +0 -283
  47. package/react/components/TimePickerPopover.d.ts +0 -18
  48. package/react/components/TimePickerPopover.js +0 -222
package/.mocharc.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "extension": ["ts", ".tsx"],
3
- "spec": "app-typescript/components/*.spec.*",
3
+ "spec": "app-typescript/**/*.spec.*",
4
4
  "require": ["ts-node/register", "./mocha-setup.ts"]
5
5
  }
@@ -192,6 +192,15 @@ angular.module('superdesk-ui.helper.dropdown', ['superdesk-ui.helper.position'])
192
192
  }
193
193
 
194
194
  $animate[isOpen ? 'addClass' : 'removeClass'](self.$element, openClass).then(() => {
195
+ // NOTE: this checks whether there is at least one dropdown open - not only this instance
196
+ const legacyAngularDropdownOpen = document.querySelector('.dropdown.open') != null;
197
+
198
+ if (legacyAngularDropdownOpen) {
199
+ document.body.classList.add('legacyAngularDropdownOpen');
200
+ } else {
201
+ document.body.classList.remove('legacyAngularDropdownOpen');
202
+ }
203
+
195
204
  if (angular.isDefined(isOpen) && isOpen !== wasOpen) {
196
205
  toggleInvoker($scope, {open: !!isOpen});
197
206
  }
@@ -372,6 +372,7 @@ $icn-button-focus-box-shadow: 0 0 0 2px $sd-colour--focus-shadow;
372
372
  [class^="icon-"], [class*=" icon-"] {
373
373
  color: var(--color-icon-default) !important;
374
374
  vertical-align: baseline !important;
375
+ pointer-events: none;
375
376
  }
376
377
 
377
378
  &:hover {
@@ -401,8 +402,10 @@ $icn-button-focus-box-shadow: 0 0 0 2px $sd-colour--focus-shadow;
401
402
  }
402
403
  }
403
404
 
404
- &--disabled {
405
+ &--disabled,
406
+ &:disabled {
405
407
  opacity: $icn-button-opacity-disabled;
408
+ pointer-events: none;
406
409
 
407
410
  &:hover, &:active, &:focus-visible {
408
411
  background-color: transparent !important;
@@ -1866,21 +1866,22 @@ h6 {
1866
1866
 
1867
1867
  // LINE HIGHT
1868
1868
  .line-height-1 {
1869
- line-height: 1;
1869
+ line-height: var(--l-height-none);
1870
1870
  }
1871
1871
  .line-height-xs {
1872
- line-height: 1.1;
1872
+ line-height: var(--l-height-xs);
1873
1873
  }
1874
1874
  .line-height-sm {
1875
- line-height: 1.2;
1875
+ line-height: var(--l-height-sm);
1876
1876
  }
1877
1877
  .line-height-md {
1878
- line-height: 1.4;
1878
+ line-height: var(--l-height-md);
1879
1879
  }
1880
1880
  .line-height-lg {
1881
- line-height: 1.5;
1881
+ line-height: var(--l-height-lg);
1882
1882
  }
1883
1883
 
1884
+
1884
1885
  // LINE CLAMP
1885
1886
  // limit the number of horizontal lines in an elemnt (e.g. <p>)
1886
1887
  // it doesn't work if the element has set flex-grow: 1;
@@ -2204,6 +2205,8 @@ h6 {
2204
2205
  container-type: inline-size;
2205
2206
  container-name: left-panel;
2206
2207
  }
2208
+
2209
+
2207
2210
  .new-form-layout {
2208
2211
  --form-group-gap: var(--gap-5);
2209
2212
  --form-layout-gap: var(--gap-4);
@@ -2216,22 +2219,38 @@ h6 {
2216
2219
  background-color: var(--color-surface-base);
2217
2220
  border-radius: var(--b-radius--large);
2218
2221
  border: 1px solid var(--color-line-light);
2219
-
2220
2222
  .new-form-heading {
2221
2223
  font-family: $baseFontFamilySerif;
2222
2224
  font-weight: 900;
2223
2225
  font-size: var(--form-heading-font-size);
2224
2226
  line-height: 1.2;
2225
2227
  }
2228
+ legend {
2229
+ float: left;
2230
+ }
2226
2231
  }
2227
2232
  .new-form-group {
2228
2233
  display: flex;
2229
2234
  gap: var(--form-group-gap);
2235
+ &.new-form-group--grid {
2236
+ display: grid;
2237
+ grid-template-columns: repeat(4, 1fr);
2238
+ gap: var(--form-group-gap);
2239
+ }
2230
2240
  }
2231
2241
  .new-form-group-item {
2232
2242
  display: flex;
2233
2243
  flex-direction: column;
2234
2244
  flex-grow: 1;
2245
+ &.new-form-group-item--span-2 {
2246
+ grid-column: span 2;
2247
+ }
2248
+ &.new-form-group-item--span-3 {
2249
+ grid-column: span 3;
2250
+ }
2251
+ &.new-form-group-item--span-4 {
2252
+ grid-column: span 4;
2253
+ }
2235
2254
  }
2236
2255
 
2237
2256
  @container left-panel (width <= 900px) {
@@ -1,9 +1,9 @@
1
1
  .sd-popover {
2
- background: var(--sd-colour-bg--05);
3
- color: #fff;
4
- padding: 8px 12px 12px;
5
- border-radius: $border-radius__base--medium;
6
- box-shadow: 0 3px 7px rgba(0,0,0,0.3);
2
+ background: var(--color-surface-base);
3
+ color: var(--color-text-default);
4
+ padding: var(--space--1) var(--space--1-5) var(--space--1-5);
5
+ border-radius: var(--b-radius--medium);
6
+ box-shadow: var(--sd-shadow--z4);
7
7
  max-width: 320px !important;
8
8
  }
9
9
 
@@ -11,15 +11,15 @@
11
11
  display: flex;
12
12
  justify-content: space-between;
13
13
  align-items: center;
14
- margin-block-end: 4px;
14
+ margin-block-end: var(--space--0-5);
15
15
  min-height: 20px;
16
16
  }
17
17
 
18
18
  .sd-popover__title {
19
- font-size: 11px;
19
+ font-size: var(--text-size-xx-small);
20
20
  line-height: 1;
21
21
  text-transform: uppercase;
22
- color: #ccc;
22
+ color: var(--color-text-muted);
23
23
  padding: 0;
24
24
  margin: 0;
25
25
  }
@@ -340,3 +340,86 @@
340
340
  .p-tooltip-right {
341
341
  margin-inline-start: 8px;
342
342
  }
343
+
344
+
345
+
346
+
347
+ // NEW TOOLTIP STYLES ----------------------------------------------------------------------------
348
+
349
+ .tooltip {
350
+ --tooltip-arrow_border: 6px;
351
+ margin: calc(var(--tooltip-arrow_border) / 2);
352
+ border-radius: var(--b-radius--medium);
353
+ padding: var(--space--1) var(--space--1-5);
354
+ font-size: var(--text-size-small);
355
+ position: relative;
356
+ background-color: var(--sd-colour-background__tooltip);
357
+ box-shadow: var(--sd-shadow--z3);
358
+ color: var(--color-text-ondark);
359
+ opacity: 1;
360
+ transition: opacity 400ms ease-out, margin 200ms ease-out;
361
+ transition-behavior: allow-discrete;
362
+
363
+ @starting-style {
364
+ opacity: 0;
365
+ }
366
+
367
+ &::before {
368
+ content: '';
369
+ border: var(--tooltip-arrow_border) solid transparent;
370
+ border-width: var(--tooltip-arrow_border);
371
+ z-index: 1001;
372
+ user-select: none;
373
+ pointer-events: none;
374
+ position: absolute;
375
+ overflow: hidden;
376
+ }
377
+ }
378
+
379
+ [data-popper-placement^="top"] {
380
+ .tooltip {
381
+ margin-block-end: var(--tooltip-arrow_border);
382
+ &::before {
383
+ inset-block-end: calc(var(--tooltip-arrow_border) * -1);
384
+ inset-inline-start: calc(50% - var(--tooltip-arrow_border));
385
+ border-bottom-width: 0;
386
+ border-top-color: var(--sd-colour-background__tooltip);
387
+ }
388
+ }
389
+ }
390
+
391
+ [data-popper-placement^="bottom"] {
392
+ .tooltip {
393
+ margin-block-start: var(--tooltip-arrow_border);
394
+ &::before {
395
+ inset-block-start: calc(var(--tooltip-arrow_border) * -1);
396
+ inset-inline-start: calc(50% - var(--tooltip-arrow_border));
397
+ border-top-width: 0;
398
+ border-bottom-color: var(--sd-colour-background__tooltip);
399
+ }
400
+ }
401
+ }
402
+
403
+ [data-popper-placement^="left"] {
404
+ .tooltip {
405
+ margin-inline-end: var(--tooltip-arrow_border);
406
+ &::before {
407
+ inset-inline-end: calc(var(--tooltip-arrow_border) * -1);
408
+ inset-block-start: calc(50% - var(--tooltip-arrow_border));
409
+ border-right-width: 0;
410
+ border-left-color: var(--sd-colour-background__tooltip);
411
+ }
412
+ }
413
+ }
414
+
415
+ [data-popper-placement^="right"] {
416
+ .tooltip {
417
+ margin-inline-start: var(--tooltip-arrow_border);
418
+ &::before {
419
+ inset-inline-start: calc(var(--tooltip-arrow_border) * -1);
420
+ inset-block-start: calc(50% - var(--tooltip-arrow_border));
421
+ border-left-width: 0;
422
+ border-right-color: var(--sd-colour-background__tooltip);
423
+ }
424
+ }
425
+ }
@@ -70,6 +70,12 @@
70
70
  --text-size-x-large: calc(var(--text-size--base) * 2.4); // 24px;
71
71
  --text-size-xx-large: calc(var(--text-size--base) * 3.2); // 32px;
72
72
 
73
+ // LINE HEIGHT
74
+ --l-height-none: 1;
75
+ --l-height-xs: 1.1;
76
+ --l-height-sm: 1.2;
77
+ --l-height-md: 1.4;
78
+ --l-height-lg: 1.5;
73
79
 
74
80
  // SHADOW
75
81
  --sd-shadow--z1: 0 0 0 1px var(--sd-shadow-outline), 0 1px 3px hsla(0, 0%, 0%, 0.16), 0 0 1px hsla(0, 0%, 0%, 0.1);
@@ -8,8 +8,9 @@ $checkButtonBorderColorHover: var(--sd-colour-line--strong);
8
8
  $checkButtonBorderRadius: $border-radius__base--small;
9
9
 
10
10
  @mixin check-base {
11
- width: 1.6rem;
12
- height: 1.6rem;
11
+ --check-size: 1.6rem;
12
+ width: var(--check-size);
13
+ height: var(--check-size);
13
14
  display: inline-block;
14
15
  position: relative;
15
16
  background: transparent;
@@ -17,6 +18,7 @@ $checkButtonBorderRadius: $border-radius__base--small;
17
18
  vertical-align: middle;
18
19
  font-family: $baseFontFamily;
19
20
  transition: all 0.2s ease-out;
21
+ flex: 0 0 var(--check-size);
20
22
  }
21
23
 
22
24
  .sd-checkbox {
@@ -132,7 +134,8 @@ $checkButtonBorderRadius: $border-radius__base--small;
132
134
  }
133
135
 
134
136
  i + label {
135
- margin-inline-start: 8px;
137
+ margin-inline-start: var(--space--1);
138
+ font-size: var(--text-size-small);
136
139
  }
137
140
 
138
141
  &::after {
@@ -383,7 +386,7 @@ $checkButtonBorderRadius: $border-radius__base--small;
383
386
  }
384
387
 
385
388
  i + .sd-check-button__text-label {
386
- margin-inline-start: $sd-base-increment;
389
+ margin-inline-start: var(--space--1);
387
390
  }
388
391
 
389
392
  &:hover {
@@ -501,7 +504,8 @@ $checkButtonBorderRadius: $border-radius__base--small;
501
504
 
502
505
  .sd-check-new + label,
503
506
  .sd-radio-new + label {
504
- margin-inline-start: $sd-base-increment;
507
+ margin-inline-start: var(--space--1);
508
+ font-size: var(--text-size-small);
505
509
  }
506
510
 
507
511
  .sd-check-new + label:empty,
@@ -781,7 +781,7 @@ input[type="color" i].sd-input__color-input::-webkit-color-swatch {
781
781
  margin: 0;
782
782
  display: grid;
783
783
  grid-template-columns: auto 1fr auto;
784
- grid-template-rows: $form-label-height auto auto;
784
+ grid-template-rows: $form-label-height auto 1fr;
785
785
  grid-gap: 0;
786
786
  position: relative;
787
787
  align-self: stretch;
@@ -940,6 +940,12 @@ input[type="color" i].sd-input__color-input::-webkit-color-swatch {
940
940
  margin: 0;
941
941
  grid-row: 1/2;
942
942
  grid-column: 2/4;
943
+ span {
944
+ overflow: hidden;
945
+ text-overflow: ellipsis;
946
+ white-space: nowrap;
947
+ max-width: 100%;
948
+ }
943
949
  }
944
950
 
945
951
  .sd-input__label--required::after {
@@ -126,7 +126,7 @@ export class Autocomplete extends React.Component<IProps, IState> {
126
126
  <div className={classes}>
127
127
  {this.props.label && !this.props.isSearchField ? (
128
128
  <label className="sd-input__label" htmlFor={this.htmlId}>
129
- {this.props.label}
129
+ <span>{this.props.label}</span>
130
130
  </label>
131
131
  ) : null}
132
132
 
@@ -95,7 +95,7 @@ export class InputWrapper extends React.Component<IProps, IState> {
95
95
  id={this.props.htmlId + 'label'}
96
96
  tabIndex={this.props.tabindex === undefined ? undefined : -1}
97
97
  >
98
- {this.props.label}
98
+ <span>{this.props.label}</span>
99
99
  </label>
100
100
  <div className="sd-input__input-container">{this.props.children}</div>
101
101
  {this.props.maxLength && (
@@ -7,6 +7,7 @@ interface IProps {
7
7
  triggerSelector: string; // CSS selector for an element that will be used to toggle the popover.
8
8
  displayCloseButton?: boolean; // defaults to true
9
9
  placement?: PopperOptions['placement']; // defaults to auto
10
+ theme?: 'light-ui' | 'dark-ui';
10
11
  }
11
12
 
12
13
  export class Popover extends React.Component<IProps> {
@@ -14,6 +15,7 @@ export class Popover extends React.Component<IProps> {
14
15
  return (
15
16
  <Positioner
16
17
  triggerSelector={this.props.triggerSelector}
18
+ theme={this.props.theme}
17
19
  placement={this.props.placement ?? 'auto'}
18
20
  className="sd-popover"
19
21
  >
@@ -171,7 +171,9 @@ export class SelectGrid extends React.PureComponent<IProps, IState> {
171
171
  aria-label={this.props.label}
172
172
  key={this.props.label}
173
173
  >
174
- <label className="sd-input__label">{this.props.label}</label>
174
+ <label className="sd-input__label">
175
+ <span>{this.props.label}</span>
176
+ </label>
175
177
  <TriggerTemplate onClick={this.mountPopup} />
176
178
  </div>
177
179
  <OverlayPanel
@@ -0,0 +1,163 @@
1
+ import {describe, it} from 'mocha';
2
+ import * as assert from 'assert';
3
+ import {convert12HourTo24Hour, convert24HourTo12Hour, toInternalState} from './TimePickerPopover';
4
+
5
+ it('should convert all 12-hour times to 24-hour format', () => {
6
+ // AM times
7
+ assert.strictEqual(convert12HourTo24Hour(12, 'am'), 0);
8
+ assert.strictEqual(convert12HourTo24Hour(1, 'am'), 1);
9
+ assert.strictEqual(convert12HourTo24Hour(2, 'am'), 2);
10
+ assert.strictEqual(convert12HourTo24Hour(3, 'am'), 3);
11
+ assert.strictEqual(convert12HourTo24Hour(4, 'am'), 4);
12
+ assert.strictEqual(convert12HourTo24Hour(5, 'am'), 5);
13
+ assert.strictEqual(convert12HourTo24Hour(6, 'am'), 6);
14
+ assert.strictEqual(convert12HourTo24Hour(7, 'am'), 7);
15
+ assert.strictEqual(convert12HourTo24Hour(8, 'am'), 8);
16
+ assert.strictEqual(convert12HourTo24Hour(9, 'am'), 9);
17
+ assert.strictEqual(convert12HourTo24Hour(10, 'am'), 10);
18
+ assert.strictEqual(convert12HourTo24Hour(11, 'am'), 11);
19
+
20
+ // PM times
21
+ assert.strictEqual(convert12HourTo24Hour(12, 'pm'), 12);
22
+ assert.strictEqual(convert12HourTo24Hour(1, 'pm'), 13);
23
+ assert.strictEqual(convert12HourTo24Hour(2, 'pm'), 14);
24
+ assert.strictEqual(convert12HourTo24Hour(3, 'pm'), 15);
25
+ assert.strictEqual(convert12HourTo24Hour(4, 'pm'), 16);
26
+ assert.strictEqual(convert12HourTo24Hour(5, 'pm'), 17);
27
+ assert.strictEqual(convert12HourTo24Hour(6, 'pm'), 18);
28
+ assert.strictEqual(convert12HourTo24Hour(7, 'pm'), 19);
29
+ assert.strictEqual(convert12HourTo24Hour(8, 'pm'), 20);
30
+ assert.strictEqual(convert12HourTo24Hour(9, 'pm'), 21);
31
+ assert.strictEqual(convert12HourTo24Hour(10, 'pm'), 22);
32
+ assert.strictEqual(convert12HourTo24Hour(11, 'pm'), 23);
33
+ });
34
+
35
+ it('should convert all 24-hour times to 12-hour format', () => {
36
+ assert.strictEqual(convert24HourTo12Hour(0), 12);
37
+ assert.strictEqual(convert24HourTo12Hour(1), 1);
38
+ assert.strictEqual(convert24HourTo12Hour(2), 2);
39
+ assert.strictEqual(convert24HourTo12Hour(3), 3);
40
+ assert.strictEqual(convert24HourTo12Hour(4), 4);
41
+ assert.strictEqual(convert24HourTo12Hour(5), 5);
42
+ assert.strictEqual(convert24HourTo12Hour(6), 6);
43
+ assert.strictEqual(convert24HourTo12Hour(7), 7);
44
+ assert.strictEqual(convert24HourTo12Hour(8), 8);
45
+ assert.strictEqual(convert24HourTo12Hour(9), 9);
46
+ assert.strictEqual(convert24HourTo12Hour(10), 10);
47
+ assert.strictEqual(convert24HourTo12Hour(11), 11);
48
+ assert.strictEqual(convert24HourTo12Hour(12), 12);
49
+ assert.strictEqual(convert24HourTo12Hour(13), 1);
50
+ assert.strictEqual(convert24HourTo12Hour(14), 2);
51
+ assert.strictEqual(convert24HourTo12Hour(15), 3);
52
+ assert.strictEqual(convert24HourTo12Hour(16), 4);
53
+ assert.strictEqual(convert24HourTo12Hour(17), 5);
54
+ assert.strictEqual(convert24HourTo12Hour(18), 6);
55
+ assert.strictEqual(convert24HourTo12Hour(19), 7);
56
+ assert.strictEqual(convert24HourTo12Hour(20), 8);
57
+ assert.strictEqual(convert24HourTo12Hour(21), 9);
58
+ assert.strictEqual(convert24HourTo12Hour(22), 10);
59
+ assert.strictEqual(convert24HourTo12Hour(23), 11);
60
+ });
61
+
62
+ describe('toInternalState', () => {
63
+ describe('null/undefined/empty input', () => {
64
+ it('should return null state for null input', () => {
65
+ assert.deepStrictEqual(toInternalState(null), {
66
+ hours: null,
67
+ minutes: null,
68
+ seconds: null,
69
+ period: null,
70
+ });
71
+ });
72
+
73
+ it('should return null state for undefined input', () => {
74
+ assert.deepStrictEqual(toInternalState(undefined), {
75
+ hours: null,
76
+ minutes: null,
77
+ seconds: null,
78
+ period: null,
79
+ });
80
+ });
81
+
82
+ it('should return null state for empty string', () => {
83
+ assert.deepStrictEqual(toInternalState(' '), {
84
+ hours: null,
85
+ minutes: null,
86
+ seconds: null,
87
+ period: null,
88
+ });
89
+ });
90
+ });
91
+
92
+ it('should convert 00:00:00 to 12:00:00 am', () => {
93
+ assert.deepStrictEqual(toInternalState('00:00:00'), {
94
+ hours: '12',
95
+ minutes: '00',
96
+ seconds: '00',
97
+ period: 'am',
98
+ });
99
+ });
100
+
101
+ it('should convert 01:30:45 to 01:30:45 am', () => {
102
+ assert.deepStrictEqual(toInternalState('01:30:45'), {
103
+ hours: '01',
104
+ minutes: '30',
105
+ seconds: '45',
106
+ period: 'am',
107
+ });
108
+ });
109
+
110
+ it('should convert 11:59:59 to 11:59:59 am', () => {
111
+ assert.deepStrictEqual(toInternalState('11:59:59'), {
112
+ hours: '11',
113
+ minutes: '59',
114
+ seconds: '59',
115
+ period: 'am',
116
+ });
117
+ });
118
+
119
+ it('should convert 12:00:00 to 12:00:00 pm', () => {
120
+ assert.deepStrictEqual(toInternalState('12:00:00'), {
121
+ hours: '12',
122
+ minutes: '00',
123
+ seconds: '00',
124
+ period: 'pm',
125
+ });
126
+ });
127
+
128
+ it('should convert 13:30:15 to 01:30:15 pm', () => {
129
+ assert.deepStrictEqual(toInternalState('13:30:15'), {
130
+ hours: '01',
131
+ minutes: '30',
132
+ seconds: '15',
133
+ period: 'pm',
134
+ });
135
+ });
136
+
137
+ it('should convert 23:59:59 to 11:59:59 pm', () => {
138
+ assert.deepStrictEqual(toInternalState('23:59:59'), {
139
+ hours: '11',
140
+ minutes: '59',
141
+ seconds: '59',
142
+ period: 'pm',
143
+ });
144
+ });
145
+
146
+ it('should handle time without seconds and default to 00', () => {
147
+ assert.deepStrictEqual(toInternalState('14:30'), {
148
+ hours: '02',
149
+ minutes: '30',
150
+ seconds: '00',
151
+ period: 'pm',
152
+ });
153
+ });
154
+
155
+ it('should handle time without seconds in morning', () => {
156
+ assert.deepStrictEqual(toInternalState('09:15'), {
157
+ hours: '09',
158
+ minutes: '15',
159
+ seconds: '00',
160
+ period: 'am',
161
+ });
162
+ });
163
+ });