From bd6cf24cd1155346f210936bb0cd58d270135c9f Mon Sep 17 00:00:00 2001 From: Jens Peters Date: Tue, 2 Jul 2024 13:06:44 +0200 Subject: [PATCH] Align table headers width For consistency across table, most visible on the dependencies page which contains two tables. --- .../site/model/DecisionsTableViewModel.kt | 8 ++- .../site/model/PropertiesTableViewModel.kt | 6 ++- .../site/model/SectionsTableViewModel.kt | 6 ++- ...SoftwareSystemDependenciesPageViewModel.kt | 4 +- .../model/SoftwareSystemTableUtilities.kt | 5 +- .../model/SoftwareSystemsPageViewModel.kt | 5 +- .../generatr/site/model/TableViewModel.kt | 46 +++++++++++----- .../site/generatr/site/views/Table.kt | 37 +++++++------ src/main/resources/assets/css/style.css | 8 +++ .../site/model/DecisionsTableViewModelTest.kt | 2 +- .../model/PropertiesTableViewModelTest.kt | 2 +- ...wareSystemDependenciesPageViewModelTest.kt | 12 ++--- .../model/SoftwareSystemsPageViewModelTest.kt | 8 +-- .../generatr/site/model/TableViewModelTest.kt | 52 +++++++++++-------- 14 files changed, 132 insertions(+), 69 deletions(-) diff --git a/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/model/DecisionsTableViewModel.kt b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/model/DecisionsTableViewModel.kt index 0193193a..bdba1ef7 100644 --- a/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/model/DecisionsTableViewModel.kt +++ b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/model/DecisionsTableViewModel.kt @@ -7,7 +7,13 @@ import nl.avisi.structurizr.site.generatr.site.formatDate fun PageViewModel.createDecisionsTableViewModel(decisions: Collection, hrefFactory: (Decision) -> String) = TableViewModel.create { - headerRow(headerCell("ID"), headerCell("Date"), headerCell("Status"), headerCell("Title")) + headerRow( + headerCellSmall("ID"), + headerCell("Date"), + headerCell("Status"), + headerCellLarge("Title") + ) + decisions .sortedBy { it.id.toInt() } .forEach { diff --git a/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/model/PropertiesTableViewModel.kt b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/model/PropertiesTableViewModel.kt index 97c5f21a..9b7af257 100644 --- a/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/model/PropertiesTableViewModel.kt +++ b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/model/PropertiesTableViewModel.kt @@ -4,7 +4,11 @@ import com.structurizr.util.Url fun createPropertiesTableViewModel(properties: Map) = TableViewModel.create { - headerRow(headerCell("Name"), headerCell("Value")) + headerRow( + headerCellMedium("Name"), + headerCell("Value") + ) + properties .toSortedMap() .forEach { (name, value) -> diff --git a/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/model/SectionsTableViewModel.kt b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/model/SectionsTableViewModel.kt index 8c473859..4aa57148 100644 --- a/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/model/SectionsTableViewModel.kt +++ b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/model/SectionsTableViewModel.kt @@ -7,7 +7,11 @@ import nl.avisi.structurizr.site.generatr.hasSections fun PageViewModel.createSectionsTableViewModel(sections: Collection
, dropFirst: Boolean = true, hrefFactory: (Section) -> String) = TableViewModel.create { - headerRow(headerCell("#"), headerCell("Title")) + headerRow( + headerCellSmall("#"), + headerCell("Title") + ) + sections .sortedBy { it.order } .drop(if (dropFirst) 1 else 0) diff --git a/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/model/SoftwareSystemDependenciesPageViewModel.kt b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/model/SoftwareSystemDependenciesPageViewModel.kt index 8da8c365..ff349ab7 100644 --- a/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/model/SoftwareSystemDependenciesPageViewModel.kt +++ b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/model/SoftwareSystemDependenciesPageViewModel.kt @@ -37,8 +37,8 @@ class SoftwareSystemDependenciesPageViewModel( private fun TableViewModel.TableViewInitializerContext.header() { headerRow( - headerCell("System"), - headerCell("Description"), + headerCellMedium("System"), + headerCellLarge("Description"), headerCell("Technology") ) } diff --git a/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/model/SoftwareSystemTableUtilities.kt b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/model/SoftwareSystemTableUtilities.kt index be75c605..4b9539ce 100644 --- a/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/model/SoftwareSystemTableUtilities.kt +++ b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/model/SoftwareSystemTableUtilities.kt @@ -1,16 +1,15 @@ package nl.avisi.structurizr.site.generatr.site.model -import com.structurizr.model.Location import com.structurizr.model.SoftwareSystem fun TableViewModel.TableViewInitializerContext.softwareSystemCell( pageViewModel: PageViewModel, system: SoftwareSystem ) = if (pageViewModel.includedSoftwareSystems.contains(system)) - headerCellWithLink( + cellWithSoftwareSystemLink( pageViewModel, system.name, SoftwareSystemPageViewModel.url(system, SoftwareSystemPageViewModel.Tab.HOME) ) else - headerCell("${system.name} (External)", greyText = true) + cellWithExternalSoftwareSystem("${system.name} (External)") diff --git a/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/model/SoftwareSystemsPageViewModel.kt b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/model/SoftwareSystemsPageViewModel.kt index 93dec43f..5bbd493c 100644 --- a/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/model/SoftwareSystemsPageViewModel.kt +++ b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/model/SoftwareSystemsPageViewModel.kt @@ -7,7 +7,10 @@ class SoftwareSystemsPageViewModel(generatorContext: GeneratorContext) : PageVie override val pageSubTitle = "Software Systems" val softwareSystemsTable: TableViewModel = TableViewModel.create { - headerRow(headerCell("Name"), headerCell("Description")) + headerRow( + headerCellMedium("Name"), + headerCell("Description") + ) generatorContext.workspace.model.softwareSystems .sortedBy { it.name.lowercase() } diff --git a/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/model/TableViewModel.kt b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/model/TableViewModel.kt index efbfac15..ebd0c146 100644 --- a/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/model/TableViewModel.kt +++ b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/model/TableViewModel.kt @@ -1,5 +1,12 @@ package nl.avisi.structurizr.site.generatr.site.model +enum class CellWidth { + UNSPECIFIED, + ONE_TENTH, + ONE_FOURTH, + TWO_FOURTH, +} + data class TableViewModel(val headerRows: List, val bodyRows: List) { sealed interface CellViewModel { val isHeader: Boolean @@ -10,16 +17,23 @@ data class TableViewModel(val headerRows: List, val bodyRows: List override val isHeader: Boolean, val greyText: Boolean = false, val boldText: Boolean = false, - val oneTenthWidth: Boolean = false + val width: CellWidth = CellWidth.UNSPECIFIED ) : CellViewModel { override fun toString() = if (isHeader) "headerCell($title, greyText=$greyText)" else - "cell($title, greyText=$greyText, boldText=$boldText, oneTenthWidth=$oneTenthWidth)" + "cell($title, greyText=$greyText, boldText=$boldText, width=${width.name})" } - data class LinkCellViewModel(val link: LinkViewModel, override val isHeader: Boolean) : CellViewModel { - override fun toString() = if (isHeader) "headerCell($link)" else "cell($link)" + data class LinkCellViewModel( + val link: LinkViewModel, + override val isHeader: Boolean, + val boldText: Boolean = false + ) : CellViewModel { + override fun toString() = if (isHeader) + "headerCell($link), boldText=$boldText," + else + "cell($link), boldText=$boldText," } data class ExternalLinkCellViewModel( @@ -46,17 +60,25 @@ data class TableViewModel(val headerRows: List, val bodyRows: List bodyRows.add(RowViewModel(cells.toList())) } - fun headerCell(title: String, greyText: Boolean = false) = TextCellViewModel(title, true, greyText) - fun headerCellWithLink(pageViewModel: PageViewModel, title: String, href: String) = - LinkCellViewModel(LinkViewModel(pageViewModel, title, href), true) - - fun cell(title: String): TextCellViewModel = TextCellViewModel(title, false) - fun cellWithIndex(title: String): TextCellViewModel = - TextCellViewModel(title, false, greyText = false, boldText = true, oneTenthWidth = true) + fun headerCell(title: String) = + TextCellViewModel(title, true) + fun headerCellSmall(title: String): TextCellViewModel = + TextCellViewModel(title, isHeader = true, width = CellWidth.ONE_TENTH) + fun headerCellMedium(title: String): TextCellViewModel = + TextCellViewModel(title, isHeader = true, width = CellWidth.ONE_FOURTH) + fun headerCellLarge(title: String): TextCellViewModel = + TextCellViewModel(title, isHeader = true, width = CellWidth.TWO_FOURTH) + fun cell(title: String, greyText: Boolean = false) = + TextCellViewModel(title, false, greyText, false) + fun cellWithIndex(title: String) = + TextCellViewModel(title, false, boldText = true) + fun cellWithExternalSoftwareSystem(title: String) = + TextCellViewModel(title, false, greyText = true, boldText = true) fun cellWithLink(pageViewModel: PageViewModel, title: String, href: String) = LinkCellViewModel(LinkViewModel(pageViewModel, title, href), false) - + fun cellWithSoftwareSystemLink(pageViewModel: PageViewModel, title: String, href: String) = + LinkCellViewModel(LinkViewModel(pageViewModel, title, href), false, boldText = true) fun cellWithExternalLink(title: String, href: String) = ExternalLinkCellViewModel(ExternalLinkViewModel(title, href), false) } diff --git a/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/views/Table.kt b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/views/Table.kt index 21e24a1e..bc78eaf5 100644 --- a/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/views/Table.kt +++ b/src/main/kotlin/nl/avisi/structurizr/site/generatr/site/views/Table.kt @@ -2,6 +2,7 @@ package nl.avisi.structurizr.site.generatr.site.views import kotlinx.html.* import nl.avisi.structurizr.site.generatr.site.model.TableViewModel +import nl.avisi.structurizr.site.generatr.site.model.CellWidth fun FlowContent.table(viewModel: TableViewModel) { table (classes = "table is-fullwidth") { @@ -36,24 +37,30 @@ private fun TBODY.row(viewModel: TableViewModel.RowViewModel) { private fun TR.cell(viewModel: TableViewModel.CellViewModel) { when (viewModel) { - is TableViewModel.TextCellViewModel -> - if (viewModel.isHeader && viewModel.greyText) - th { span(classes = "has-text-grey") { +viewModel.title } } - else if (viewModel.isHeader) - th { +viewModel.title } - else if (viewModel.boldText && viewModel.oneTenthWidth) - td(classes = "is-one-tenth") { span(classes = "has-text-weight-bold") { +viewModel.title } } - else if (viewModel.boldText) - td { span(classes = "has-text-weight-bold") { +viewModel.title } } - else if (viewModel.oneTenthWidth) - td(classes = "is-one-tenth") { +viewModel.title } + is TableViewModel.TextCellViewModel -> { + val classes = when (viewModel.width) { + CellWidth.UNSPECIFIED -> null + CellWidth.ONE_TENTH -> "is-one-tenth" + CellWidth.ONE_FOURTH -> "is-one-fourth" + CellWidth.TWO_FOURTH -> "is-two-fourth" + } + + var spanClasses = "" + if (viewModel.greyText) spanClasses += "has-text-grey " + if (viewModel.boldText) spanClasses += "has-text-weight-bold" + + if (viewModel.isHeader) + th(classes = classes) { span(classes = spanClasses) { +viewModel.title } } else - td { +viewModel.title } - is TableViewModel.LinkCellViewModel -> + td(classes = classes) { span(classes = spanClasses) { +viewModel.title } } + } + is TableViewModel.LinkCellViewModel -> { + val classes = if (viewModel.boldText) "has-text-weight-bold" else null if (viewModel.isHeader) - th { link(viewModel.link) } + th(classes = classes) { link(viewModel.link) } else - td { link(viewModel.link) } + td(classes = classes) { link(viewModel.link) } + } is TableViewModel.ExternalLinkCellViewModel -> if (viewModel.isHeader) th { externalLink(viewModel.link) } diff --git a/src/main/resources/assets/css/style.css b/src/main/resources/assets/css/style.css index e50d09d7..b6e6a5fe 100644 --- a/src/main/resources/assets/css/style.css +++ b/src/main/resources/assets/css/style.css @@ -22,6 +22,14 @@ width: 10%; } +.is-one-fourth { + width: 25%; +} + +.is-two-fourth { + width: 50%; +} + svg a:hover { opacity: 90%; } diff --git a/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/model/DecisionsTableViewModelTest.kt b/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/model/DecisionsTableViewModelTest.kt index 337193fa..4a843ab2 100644 --- a/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/model/DecisionsTableViewModelTest.kt +++ b/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/model/DecisionsTableViewModelTest.kt @@ -44,6 +44,6 @@ class DecisionsTableViewModelTest : ViewModelTest() { } private fun TableViewModel.TableViewInitializerContext.decisionsTableHeaderRow() { - headerRow(headerCell("ID"), headerCell("Date"), headerCell("Status"), headerCell("Title")) + headerRow(headerCellSmall("ID"), headerCell("Date"), headerCell("Status"), headerCellLarge("Title")) } } diff --git a/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/model/PropertiesTableViewModelTest.kt b/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/model/PropertiesTableViewModelTest.kt index 9133a25e..9c73f7a3 100644 --- a/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/model/PropertiesTableViewModelTest.kt +++ b/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/model/PropertiesTableViewModelTest.kt @@ -56,6 +56,6 @@ class PropertiesTableViewModelTest : ViewModelTest() { } private fun TableViewModel.TableViewInitializerContext.propertiesTableHeaderRow() { - headerRow(headerCell("Name"), headerCell("Value")) + headerRow(headerCellMedium("Name"), headerCell("Value")) } } diff --git a/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/model/SoftwareSystemDependenciesPageViewModelTest.kt b/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/model/SoftwareSystemDependenciesPageViewModelTest.kt index cb70cfc2..b085c7e6 100644 --- a/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/model/SoftwareSystemDependenciesPageViewModelTest.kt +++ b/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/model/SoftwareSystemDependenciesPageViewModelTest.kt @@ -37,7 +37,7 @@ class SoftwareSystemDependenciesPageViewModelTest : ViewModelTest() { TableViewModel.create { dependenciesTableHeader() bodyRow( - headerCellWithLink( + cellWithSoftwareSystemLink( viewModel, softwareSystem2.name, SoftwareSystemPageViewModel.url(softwareSystem2, SoftwareSystemPageViewModel.Tab.HOME) ), @@ -57,7 +57,7 @@ class SoftwareSystemDependenciesPageViewModelTest : ViewModelTest() { TableViewModel.create { dependenciesTableHeader() bodyRow( - headerCellWithLink( + cellWithSoftwareSystemLink( viewModel, softwareSystem2.name, SoftwareSystemPageViewModel.url(softwareSystem2, SoftwareSystemPageViewModel.Tab.HOME) ), @@ -99,9 +99,9 @@ class SoftwareSystemDependenciesPageViewModelTest : ViewModelTest() { val viewModel = SoftwareSystemDependenciesPageViewModel(generatorContext, softwareSystem1) assertThat(viewModel.dependenciesInboundTable.bodyRows[0].columns[0]) - .isEqualTo(TableViewModel.TextCellViewModel("External system (External)", isHeader = true, greyText = true)) + .isEqualTo(TableViewModel.TextCellViewModel("External system (External)", isHeader = false, greyText = true, boldText = true)) assertThat(viewModel.dependenciesOutboundTable.bodyRows[0].columns[0]) - .isEqualTo(TableViewModel.TextCellViewModel("External system (External)", isHeader = true, greyText = true)) + .isEqualTo(TableViewModel.TextCellViewModel("External system (External)", isHeader = false, greyText = true, boldText = true)) } @Test @@ -122,8 +122,8 @@ class SoftwareSystemDependenciesPageViewModelTest : ViewModelTest() { private fun TableViewModel.TableViewInitializerContext.dependenciesTableHeader() { headerRow( - headerCell("System"), - headerCell("Description"), + headerCellMedium("System"), + headerCellLarge("Description"), headerCell("Technology"), ) } diff --git a/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/model/SoftwareSystemsPageViewModelTest.kt b/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/model/SoftwareSystemsPageViewModelTest.kt index be0c730c..b7825819 100644 --- a/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/model/SoftwareSystemsPageViewModelTest.kt +++ b/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/model/SoftwareSystemsPageViewModelTest.kt @@ -29,9 +29,9 @@ class SoftwareSystemsPageViewModelTest : ViewModelTest() { assertThat(viewModel.softwareSystemsTable).isEqualTo( TableViewModel.create { - headerRow(headerCell("Name"), headerCell("Description")) + headerRow(headerCellMedium("Name"), headerCell("Description")) bodyRow( - headerCellWithLink( + cellWithSoftwareSystemLink( viewModel, system1.name, SoftwareSystemPageViewModel.url( system1, SoftwareSystemPageViewModel.Tab.HOME @@ -40,7 +40,7 @@ class SoftwareSystemsPageViewModelTest : ViewModelTest() { cell(system1.description) ) bodyRow( - headerCellWithLink( + cellWithSoftwareSystemLink( viewModel, system2.name, SoftwareSystemPageViewModel.url( system2, SoftwareSystemPageViewModel.Tab.HOME @@ -77,6 +77,6 @@ class SoftwareSystemsPageViewModelTest : ViewModelTest() { val viewModel = SoftwareSystemsPageViewModel(generatorContext) assertThat(viewModel.softwareSystemsTable.bodyRows[1].columns[0]) - .isEqualTo(TableViewModel.TextCellViewModel("system 2 (External)", isHeader = true, greyText = true)) + .isEqualTo(TableViewModel.TextCellViewModel("system 2 (External)", isHeader = false, greyText = true, boldText = true)) } } diff --git a/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/model/TableViewModelTest.kt b/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/model/TableViewModelTest.kt index f60caa07..da5d1603 100644 --- a/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/model/TableViewModelTest.kt +++ b/src/test/kotlin/nl/avisi/structurizr/site/generatr/site/model/TableViewModelTest.kt @@ -1,7 +1,6 @@ package nl.avisi.structurizr.site.generatr.site.model import assertk.assertThat -import assertk.assertions.containsAll import assertk.assertions.containsAtLeast import kotlin.test.Test @@ -29,59 +28,71 @@ class TableViewModelTest : ViewModelTest() { } @Test - fun `header cell with grey text`() { + fun `header row with index`() { val viewModel = TableViewModel.create { - headerRow(headerCell("1", greyText = true)) + headerRow(headerCellSmall("1")) } assertThat(viewModel.headerRows).containsAtLeast( TableViewModel.RowViewModel( listOf( - TableViewModel.TextCellViewModel("1", isHeader = true, greyText = true), + TableViewModel.TextCellViewModel("1", isHeader = true, width = CellWidth.ONE_TENTH) ) ) ) } @Test - fun `body rows`() { + fun `header row with name`() { val viewModel = TableViewModel.create { - bodyRow(cell("1"), cell("2"), cell("3")) + headerRow(headerCellMedium("Name")) } - assertThat(viewModel.bodyRows).containsAtLeast( + assertThat(viewModel.headerRows).containsAtLeast( TableViewModel.RowViewModel( listOf( - TableViewModel.TextCellViewModel("1", isHeader = false), - TableViewModel.TextCellViewModel("2", isHeader = false), - TableViewModel.TextCellViewModel("3", isHeader = false), + TableViewModel.TextCellViewModel("Name", isHeader = true, width = CellWidth.ONE_FOURTH) ) ) ) } @Test - fun `cell with link`() { + fun `header row with description`() { val viewModel = TableViewModel.create { - bodyRow(cellWithLink(pageViewModel, "click me", "/decisions")) + headerRow(headerCellLarge("Description")) + } + + assertThat(viewModel.headerRows).containsAtLeast( + TableViewModel.RowViewModel( + listOf( + TableViewModel.TextCellViewModel("Description", isHeader = true, width = CellWidth.TWO_FOURTH) + ) + ) + ) + } + + @Test + fun `body rows`() { + val viewModel = TableViewModel.create { + bodyRow(cell("1"), cell("2"), cell("3")) } assertThat(viewModel.bodyRows).containsAtLeast( TableViewModel.RowViewModel( listOf( - TableViewModel.LinkCellViewModel( - LinkViewModel(pageViewModel, "click me", "/decisions"), - isHeader = false - ) + TableViewModel.TextCellViewModel("1", isHeader = false), + TableViewModel.TextCellViewModel("2", isHeader = false), + TableViewModel.TextCellViewModel("3", isHeader = false), ) ) ) } @Test - fun `header cell with link`() { + fun `cell with link`() { val viewModel = TableViewModel.create { - bodyRow(headerCellWithLink(pageViewModel, "click me", "/decisions")) + bodyRow(cellWithLink(pageViewModel, "click me", "/decisions")) } assertThat(viewModel.bodyRows).containsAtLeast( @@ -89,7 +100,7 @@ class TableViewModelTest : ViewModelTest() { listOf( TableViewModel.LinkCellViewModel( LinkViewModel(pageViewModel, "click me", "/decisions"), - isHeader = true + isHeader = false ) ) ) @@ -127,8 +138,7 @@ class TableViewModelTest : ViewModelTest() { "1", isHeader = false, greyText = false, - boldText = true, - oneTenthWidth = true + boldText = true ) ) )