Project

General

Profile

Defect #44595 » 0001-Fix-disappearing-Gantt-bars-in-responsive-layouts-and-oversized-progress-segments-at-low-percentages.patch

Katsuya HIDAKA, 2026-10-11 09:33

View differences:

app/assets/stylesheets/gantt.css
248 248
  inset-inline-start: var(--gantt-task-start);
249 249
  inline-size: var(--gantt-task-width);
250 250
  block-size: 8px;
251
  box-sizing: border-box;
251 252
  font-size: 0.8em;
252 253
  color: var(--oc-gray-6);
253 254
  padding: 0;
......
277 278
}
278 279

  
279 280
.gantt-task-late {
281
  block-size: 10px;
280 282
  background: var(--oc-red-5) url(/task_late.png);
281 283
  border: 1px solid var(--oc-red-5);
282 284
}
283 285

  
284 286
.gantt-task-done {
287
  block-size: 10px;
285 288
  background: var(--oc-green-7) url(/task_done.png);
286 289
  border: 1px solid var(--oc-green-7);
287 290
}
288 291

  
289 292
.gantt-task-todo {
293
  block-size: 10px;
290 294
  background: var(--oc-gray-5) url(/task_todo.png);
291 295
  border: 1px solid var(--oc-gray-5);
292 296
}
......
294 298
.gantt-task-todo.gantt-task-parent {
295 299
  background: var(--oc-gray-6);
296 300
  border: 1px solid var(--oc-gray-6);
297
  block-size: 3px;
301
  block-size: 5px;
298 302
}
299 303

  
300 304
.gantt-task-late.gantt-task-parent,
301 305
.gantt-task-done.gantt-task-parent {
302
  block-size: 3px;
306
  block-size: 5px;
303 307
}
304 308

  
305 309
.gantt-task.gantt-task-parent.gantt-task-marker {
......
313 317
.gantt-row[data-gantt-row-type="version"] .gantt-task-late {
314 318
  background: var(--oc-red-5) url(/milestone_late.png);
315 319
  border: 1px solid var(--oc-red-5);
316
  block-size: 2px;
320
  block-size: 4px;
317 321
  margin-block-start: 3px;
318 322
}
319 323

  
320 324
.gantt-row[data-gantt-row-type="version"] .gantt-task-done {
321 325
  background: var(--oc-green-7) url(/milestone_done.png);
322 326
  border: 1px solid var(--oc-green-7);
323
  block-size: 2px;
327
  block-size: 4px;
324 328
  margin-block-start: 3px;
325 329
}
326 330

  
327 331
.gantt-row[data-gantt-row-type="version"] .gantt-task-todo {
328 332
  background: var(--oc-white) url(/milestone_todo.png);
329 333
  border: 1px solid var(--oc-white);
330
  block-size: 2px;
334
  block-size: 4px;
331 335
  margin-block-start: 3px;
332 336
}
333 337

  
......
342 346
.gantt-row[data-gantt-row-type="project"] .gantt-task-late {
343 347
  background: var(--oc-red-5) url(/milestone_late.png);
344 348
  border: 1px solid var(--oc-red-5);
345
  block-size: 2px;
349
  block-size: 4px;
346 350
  margin-block-start: 3px;
347 351
}
348 352

  
349 353
.gantt-row[data-gantt-row-type="project"] .gantt-task-done {
350 354
  background: var(--oc-green-7) url(/milestone_done.png);
351 355
  border: 1px solid var(--oc-green-7);
352
  block-size: 2px;
356
  block-size: 4px;
353 357
  margin-block-start: 3px;
354 358
}
355 359

  
356 360
.gantt-row[data-gantt-row-type="project"] .gantt-task-todo {
357 361
  background: var(--oc-white) url(/milestone_todo.png);
358 362
  border: 1px solid var(--oc-white);
359
  block-size: 2px;
363
  block-size: 4px;
360 364
  margin-block-start: 3px;
361 365
}
362 366

  
lib/redmine/helpers/gantt.rb
931 931
        task_classes << 'gantt-task-parent' if object.is_a?(Issue) && !object.leaf?
932 932
        # Renders the task bar, with progress and late
933 933
        if coords[:bar_start] && coords[:bar_end]
934
          width = coords[:bar_end] - coords[:bar_start] - 2
934
          width = coords[:bar_end] - coords[:bar_start]
935 935
          style = task_style(coords[:bar_start], width)
936 936
          todo_data = {}
937 937
          if object.is_a?(Issue)
......
944 944
                         :data => todo_data}
945 945
          output << view.content_tag(:div, '&nbsp;'.html_safe, content_opt)
946 946
          if coords[:bar_late_end]
947
            width = coords[:bar_late_end] - coords[:bar_start] - 2
947
            width = coords[:bar_late_end] - coords[:bar_start]
948 948
            style = task_style(coords[:bar_start], width)
949 949
            output << view.content_tag(:div, '&nbsp;'.html_safe,
950 950
                                       :style => style,
951 951
                                       :class => (task_classes + ['gantt-task-late']).join(' '))
952 952
          end
953 953
          if coords[:bar_progress_end]
954
            width = coords[:bar_progress_end] - coords[:bar_start] - 2
954
            width = coords[:bar_progress_end] - coords[:bar_start]
955 955
            style = task_style(coords[:bar_start], width)
956 956
            output << view.content_tag(:div, '&nbsp;'.html_safe,
957 957
                                       :style => style,
test/functional/gantts_controller_test.rb
252 252

  
253 253
    # eCookbook
254 254
    assert_subject_row('project-1', row: '0', text: project.name)
255
    assert_chart_row('div.gantt-task-todo', row: '0', style_substring: 'inset-inline-start:0px;width:138px')
255
    assert_chart_row('div.gantt-task-todo', row: '0', style_substring: 'inset-inline-start:0px;width:140px')
256 256

  
257 257
    assert_issue_row(3, 'Bug #3', row: '1')
258
    assert_chart_row('div.gantt-task-todo', row: '1', style_substring: 'inset-inline-start:0px;width:38px')
258
    assert_chart_row('div.gantt-task-todo', row: '1', style_substring: 'inset-inline-start:0px;width:40px')
259 259

  
260 260
    assert_issue_row(7, 'Bug #7', row: '2')
261
    assert_chart_row('div.gantt-task-todo', row: '2', style_substring: 'inset-inline-start:16px;width:42px')
261
    assert_chart_row('div.gantt-task-todo', row: '2', style_substring: 'inset-inline-start:16px;width:44px')
262 262

  
263 263
    assert_issue_row(1, 'Bug #1', row: '3')
264
    assert_chart_row('div.gantt-task-todo', row: '3', style_substring: 'inset-inline-start:52px;width:46px')
264
    assert_chart_row('div.gantt-task-todo', row: '3', style_substring: 'inset-inline-start:52px;width:48px')
265 265

  
266 266
    # Version 1.0
267 267
    assert_subject_row('version-2-project-1', row: '4', text: '1.0')
268
    assert_chart_row('div.gantt-task-todo', row: '4', style_substring: 'inset-inline-start:48px;width:90px')
268
    assert_chart_row('div.gantt-task-todo', row: '4', style_substring: 'inset-inline-start:48px;width:92px')
269 269

  
270 270
    assert_issue_row(2, 'Feature request #2', row: '5')
271
    assert_chart_row('div.gantt-task-todo', row: '5', style_substring: 'inset-inline-start:48px;width:90px')
271
    assert_chart_row('div.gantt-task-todo', row: '5', style_substring: 'inset-inline-start:48px;width:92px')
272 272

  
273 273
    # Private child of eCookbook
274 274
    assert_subject_row(
......
276 276
      row: '6',
277 277
      text: projects(:projects_005).name
278 278
    )
279
    assert_chart_row('div.gantt-task-todo', row: '6', style_substring: 'inset-inline-start:56px;width:6px')
279
    assert_chart_row('div.gantt-task-todo', row: '6', style_substring: 'inset-inline-start:56px;width:8px')
280 280

  
281 281
    assert_issue_row(6, 'Bug #6', row: '7')
282
    assert_chart_row('div.gantt-task-todo', row: '7', style_substring: 'inset-inline-start:56px;width:6px')
282
    assert_chart_row('div.gantt-task-todo', row: '7', style_substring: 'inset-inline-start:56px;width:8px')
283 283

  
284 284
    assert_issue_row(9, 'Bug #9', row: '8')
285
    assert_chart_row('div.gantt-task-todo', row: '8', style_substring: 'inset-inline-start:56px;width:6px')
285
    assert_chart_row('div.gantt-task-todo', row: '8', style_substring: 'inset-inline-start:56px;width:8px')
286 286

  
287 287
    assert_issue_row(10, 'Bug #10', row: '9')
288
    assert_chart_row('div.gantt-task-todo', row: '9', style_substring: 'inset-inline-start:56px;width:6px')
288
    assert_chart_row('div.gantt-task-todo', row: '9', style_substring: 'inset-inline-start:56px;width:8px')
289 289
    assert_select 'div.gantt-task-todo[data-gantt-issue-id=?][data-gantt-relations*=?]', '10', '9'
290 290

  
291 291
    # eCookbook Subproject1
......
327 327

  
328 328
    # eCookbook
329 329
    assert_subject_row('project-1', row: '0', text: projects(:projects_001).name)
330
    assert_chart_row('div.gantt-task-todo', row: '0', style_substring: 'inset-inline-start:0px;width:258px')
330
    assert_chart_row('div.gantt-task-todo', row: '0', style_substring: 'inset-inline-start:0px;width:260px')
331 331

  
332 332
    # Private child of eCookbook
333 333
    assert_subject_row(
......
335 335
      row: '1',
336 336
      text: project.name
337 337
    )
338
    assert_chart_row('div.gantt-task-todo', row: '1', style_substring: 'inset-inline-start:176px;width:6px')
338
    assert_chart_row('div.gantt-task-todo', row: '1', style_substring: 'inset-inline-start:176px;width:8px')
339 339

  
340 340
    # Bug #6
341 341
    assert_issue_row(6, 'Bug #6', row: '2')
342
    assert_chart_row('div.gantt-task-todo', row: '2', style_substring: 'inset-inline-start:176px;width:6px')
342
    assert_chart_row('div.gantt-task-todo', row: '2', style_substring: 'inset-inline-start:176px;width:8px')
343 343

  
344 344
    # Bug #9
345 345
    assert_issue_row(9, 'Bug #9', row: '3')
346
    assert_chart_row('div.gantt-task-todo', row: '3', style_substring: 'inset-inline-start:176px;width:6px')
346
    assert_chart_row('div.gantt-task-todo', row: '3', style_substring: 'inset-inline-start:176px;width:8px')
347 347

  
348 348
    # Bug #10
349 349
    assert_issue_row(10, 'Bug #10', row: '4')
350
    assert_chart_row('div.gantt-task-todo', row: '4', style_substring: 'inset-inline-start:176px;width:6px')
350
    assert_chart_row('div.gantt-task-todo', row: '4', style_substring: 'inset-inline-start:176px;width:8px')
351 351

  
352 352
    assert_select 'div.gantt-task-todo[data-gantt-issue-id=?][data-gantt-relations*=?]', '10', '9'
353 353
  end
test/unit/lib/redmine/helpers/gantt_test.rb
392 392
      @output_buffer = @gantt.line(start_date, gantt_start, 30, false, 'line', :format => :html, :zoom => 4)
393 393
      # the leftmost date (Date.today - 14 days)
394 394
      assert_select 'div.gantt-task-todo[style*="--gantt-task-start:0px"]', 1, @output_buffer
395
      assert_select 'div.gantt-task-todo[style*="--gantt-task-width:2px"]', 1, @output_buffer
395
      assert_select 'div.gantt-task-todo[style*="--gantt-task-width:4px"]', 1, @output_buffer
396 396
    end
397 397
  end
398 398

  
......
402 402
      @output_buffer = @gantt.line(gantt_end, end_date, 30, false, 'line', :format => :html, :zoom => 4)
403 403
      # the rightmost date (Date.today + 14 days)
404 404
      assert_select 'div.gantt-task-todo[style*="--gantt-task-start:112px"]', 1, @output_buffer
405
      assert_select 'div.gantt-task-todo[style*="--gantt-task-width:2px"]', 1, @output_buffer
405
      assert_select 'div.gantt-task-todo[style*="--gantt-task-width:4px"]', 1, @output_buffer
406 406
    end
407 407
  end
408 408

  
409 409
  test "#line todo line should be the total width" do
410 410
    create_gantt
411 411
    @output_buffer = @gantt.line(today - 7, today + 7, 30, false, 'line', :format => :html, :zoom => 4)
412
    assert_select 'div.gantt-task-todo[style*="--gantt-task-width:58px"]', 1
412
    assert_select 'div.gantt-task-todo[style*="--gantt-task-width:60px"]', 1
413 413
  end
414 414

  
415 415
  test "#line late line should start from the starting point on the left" do
......
421 421
  test "#line late line should be the total delayed width" do
422 422
    create_gantt
423 423
    @output_buffer = @gantt.line(today - 7, today + 7, 30, false, 'line', :format => :html, :zoom => 4)
424
    assert_select 'div.gantt-task-late[style*="--gantt-task-width:30px"]', 1
424
    assert_select 'div.gantt-task-late[style*="--gantt-task-width:32px"]', 1
425 425
  end
426 426

  
427 427
  test "#line late line should be the same width as task_todo if start date and end date are the same day" do
428 428
    create_gantt
429 429
    @output_buffer = @gantt.line(today - 7, today - 7, 0, false, 'line', :format => :html, :zoom => 4)
430
    assert_select 'div.gantt-task-late[style*="--gantt-task-width:2px"]', 1
431
    assert_select 'div.gantt-task-todo[style*="--gantt-task-width:2px"]', 1
430
    assert_select 'div.gantt-task-late[style*="--gantt-task-width:4px"]', 1
431
    assert_select 'div.gantt-task-todo[style*="--gantt-task-width:4px"]', 1
432 432
  end
433 433

  
434 434
  test "#line late line should be the same width as task_todo if start date and today are the same day" do
435 435
    create_gantt
436 436
    @output_buffer = @gantt.line(today, today, 0, false, 'line', :format => :html, :zoom => 4)
437
    assert_select 'div.gantt-task-late[style*="--gantt-task-width:2px"]', 1
438
    assert_select 'div.gantt-task-todo[style*="--gantt-task-width:2px"]', 1
437
    assert_select 'div.gantt-task-late[style*="--gantt-task-width:4px"]', 1
438
    assert_select 'div.gantt-task-todo[style*="--gantt-task-width:4px"]', 1
439 439
  end
440 440

  
441 441
  test "#line done line should start from the starting point on the left" do
......
447 447
  test "#line done line should be the width for the done ratio" do
448 448
    create_gantt
449 449
    @output_buffer = @gantt.line(today - 7, today + 7, 30, false, 'line', :format => :html, :zoom => 4)
450
    # 15 days * 4 px * 30% - 2 px for borders = 16 px
451
    assert_select 'div.gantt-task-done[style*="--gantt-task-width:16px"]', 1
450
    # 15 days * 4 px * 30% = 18 px
451
    assert_select 'div.gantt-task-done[style*="--gantt-task-width:18px"]', 1
452 452
  end
453 453

  
454 454
  test "#line done line should be the total width for 100% done ratio" do
455 455
    create_gantt
456 456
    @output_buffer = @gantt.line(today - 7, today + 7, 100, false, 'line', :format => :html, :zoom => 4)
457
    # 15 days * 4 px - 2 px for borders = 58 px
458
    assert_select 'div.gantt-task-done[style*="--gantt-task-width:58px"]', 1
457
    # 15 days * 4 px = 60 px
458
    assert_select 'div.gantt-task-done[style*="--gantt-task-width:60px"]', 1
459 459
  end
460 460

  
461 461
  test "#line done line should be the total width for 100% done ratio with same start and end dates" do
462 462
    create_gantt
463 463
    @output_buffer = @gantt.line(today + 7, today + 7, 100, false, 'line', :format => :html, :zoom => 4)
464
    assert_select 'div.gantt-task-done[style*="--gantt-task-width:2px"]', 1
464
    assert_select 'div.gantt-task-done[style*="--gantt-task-width:4px"]', 1
465 465
  end
466 466

  
467 467
  test "#line done line should not be the total done width if the gantt starts after start date" do
468 468
    create_gantt
469 469
    @output_buffer = @gantt.line(today - 16, today - 2, 30, false, 'line', :format => :html, :zoom => 4)
470 470
    assert_select 'div.gantt-task-done[style*="--gantt-task-start:0px"]', 1
471
    assert_select 'div.gantt-task-done[style*="--gantt-task-width:8px"]', 1
471
    assert_select 'div.gantt-task-done[style*="--gantt-task-width:10px"]', 1
472
  end
473

  
474
  test "#line small done ratio should not produce a negative width" do
475
    create_gantt
476
    @output_buffer = @gantt.line(today - 7, today - 3, 1, false, 'line', :format => :html, :zoom => 2)
477
    assert_select 'div.gantt-task-done[style*="--gantt-task-width:0px"]', 1
472 478
  end
473 479

  
474 480
  test "#line starting marker should appear at the start date" do
(5-5/5)