@@ -100,7 +100,7 @@ const table: TableInfo = {
100100
101101const row : TableRow = {
102102 id : 'row-1' ,
103- data : { expires_at : Date . parse ( '2026-11-01T08 :00:00Z' ) / 1000 } ,
103+ data : { expires_at : '2026-11-01T01 :00:00-07:00' } ,
104104 executions : { } ,
105105 position : 0 ,
106106 createdAt : '2026-01-01T00:00:00Z' ,
@@ -119,7 +119,7 @@ describe('RowModal expiration editing', () => {
119119 mockUpdateRow . mockResolvedValue ( undefined )
120120 } )
121121
122- it ( 'waits for the saved timezone, freezes it, and chooses the later repeated hour ' , async ( ) => {
122+ it ( 'preserves expiration offsets while timezone settings load or change ' , async ( ) => {
123123 mockUseTimezoneState . mockReturnValue ( { timezone : 'Asia/Tokyo' , status : 'loading' } )
124124 const container = document . createElement ( 'div' )
125125 document . body . appendChild ( container )
@@ -136,12 +136,9 @@ describe('RowModal expiration editing', () => {
136136 ; ( globalThis as { IS_REACT_ACT_ENVIRONMENT ?: boolean } ) . IS_REACT_ACT_ENVIRONMENT = true
137137 act ( ( ) => root . render ( createElement ( RowModal , props ) ) )
138138
139- expect ( container . querySelector ( '[aria-label="Edit expires_at"]' ) ?. textContent ) . toBe (
140- 'Loading timezone…'
141- )
142- expect ( container . querySelector < HTMLInputElement > ( '[data-testid="time"]' ) ) . toBeNull ( )
139+ expect ( container . querySelector < HTMLInputElement > ( '[data-testid="time"]' ) ?. value ) . toBe ( '01:00' )
143140 expect ( container . querySelector < HTMLButtonElement > ( '[data-testid="submit"]' ) ?. disabled ) . toBe (
144- true
141+ false
145142 )
146143
147144 mockUseTimezoneState . mockReturnValue ( {
@@ -165,7 +162,7 @@ describe('RowModal expiration editing', () => {
165162
166163 expect ( mockUpdateRow ) . toHaveBeenCalledWith ( {
167164 rowId : 'row-1' ,
168- data : { expires_at : Date . parse ( '2026-11-01T09 :30:00Z' ) / 1000 } ,
165+ data : { expires_at : '2026-11-01T01 :30:00-07:00' } ,
169166 } )
170167 expect ( props . onSuccess ) . toHaveBeenCalledTimes ( 1 )
171168
@@ -209,7 +206,7 @@ describe('RowModal expiration editing', () => {
209206 container . remove ( )
210207 } )
211208
212- it ( 'blocks an invalid saved timezone with the plain-text guidance ' , ( ) => {
209+ it ( 'allows expiration edits even when the saved timezone is invalid ' , ( ) => {
213210 mockUseTimezoneState . mockReturnValue ( {
214211 timezone : 'America/Los_Angeles' ,
215212 savedTimezone : 'Mars/Olympus' ,
@@ -229,18 +226,11 @@ describe('RowModal expiration editing', () => {
229226
230227 act ( ( ) => root . render ( createElement ( RowModal , props ) ) )
231228
232- const blockedField = container . querySelector < HTMLButtonElement > (
233- '[aria-label="Edit expires_at"]'
234- )
235- expect ( blockedField ?. textContent ) . toBe ( String ( row . data . expires_at ) )
229+ expect ( container . querySelector < HTMLInputElement > ( '[data-testid="time"]' ) ?. value ) . toBe ( '01:00' )
236230 expect ( container . querySelector < HTMLButtonElement > ( '[data-testid="submit"]' ) ?. disabled ) . toBe (
237- true
231+ false
238232 )
239233 expect ( mockToastError ) . not . toHaveBeenCalled ( )
240- act ( ( ) => blockedField ?. click ( ) )
241- expect ( mockToastError ) . toHaveBeenCalledWith (
242- 'Your saved timezone “Mars/Olympus” is invalid. Update it in Settings → General before editing Date or Expiration cells.'
243- )
244234 act ( ( ) => root . unmount ( ) )
245235 container . remove ( )
246236 } )
@@ -259,11 +249,19 @@ describe('RowModal expiration editing', () => {
259249 schema : {
260250 columns : [
261251 { name : 'name' , type : 'string' } ,
252+ { name : 'starts_at' , type : 'date' } ,
262253 { name : 'expires_at' , type : 'ttl' } ,
263254 ] ,
264255 } ,
265256 }
266- const mixedRow = { ...row , data : { name : 'Ada' , expires_at : row . data . expires_at } }
257+ const mixedRow = {
258+ ...row ,
259+ data : {
260+ name : 'Ada' ,
261+ expires_at : row . data . expires_at ,
262+ starts_at : '2026-09-07T12:00:00-07:00' ,
263+ } ,
264+ }
267265 const props = {
268266 mode : 'edit' as const ,
269267 isOpen : true ,
@@ -276,20 +274,18 @@ describe('RowModal expiration editing', () => {
276274 act ( ( ) => root . render ( createElement ( RowModal , props ) ) )
277275
278276 const nameInput = container . querySelector < HTMLInputElement > ( '[data-testid="modal-input"]' )
279- const blockedField = container . querySelector < HTMLButtonElement > (
280- '[aria-label="Edit expires_at"]'
281- )
277+ const blockedField = container . querySelector < HTMLButtonElement > ( '[aria-label="Edit starts_at"]' )
282278 const submit = container . querySelector < HTMLButtonElement > ( '[data-testid="submit"]' )
283279 expect ( nameInput ?. value ) . toBe ( 'Ada' )
284- expect ( blockedField ?. textContent ) . toBe ( String ( row . data . expires_at ) )
280+ expect ( blockedField ?. textContent ) . toBe ( mixedRow . data . starts_at )
285281 expect ( submit ?. disabled ) . toBe ( false )
286282
287283 act ( ( ) => changeInput ( nameInput as HTMLInputElement , 'Grace' ) )
288284 await act ( async ( ) => submit ?. click ( ) )
289285
290286 expect ( mockUpdateRow ) . toHaveBeenCalledWith ( {
291287 rowId : 'row-1' ,
292- data : { name : 'Grace' } ,
288+ data : { name : 'Grace' , expires_at : row . data . expires_at } ,
293289 } )
294290 expect ( props . onSuccess ) . toHaveBeenCalledTimes ( 1 )
295291 expect ( mockToastError ) . not . toHaveBeenCalled ( )
0 commit comments