]> Piment Noir Git Repositories - e-mobility-charging-stations-simulator.git/commitdiff
fix(webui): polish the station details view (panel placement + section/key-value...
authorJérôme Benoit <jerome.benoit@piment-noir.org>
Mon, 10 Aug 2026 17:38:42 +0000 (19:38 +0200)
committerGitHub <noreply@github.com>
Mon, 10 Aug 2026 17:38:42 +0000 (19:38 +0200)
CSS/Vue-only polish of the #993 Show details view: classic panel-placement fix + section/key-value separation; modern section panels, promoted titles and tighter symmetric row separators; not-found escape; toggle-navigation test coverage.

ui/web/src/skins/classic/components/actions/ShowDetails.vue
ui/web/src/skins/modern/components/dialogs/ShowDetailsDialog.vue
ui/web/tests/unit/skins/classic/Actions.test.ts
ui/web/tests/unit/skins/classic/CSData.test.ts

index 9557b19f3c602fc0b8765193f2f8238f49be10cf..2b482cae63290fc0ecd2f96f5642d6cc49107acd 100644 (file)
@@ -3,12 +3,17 @@
     Show Details
   </h1>
   <h2>{{ chargingStationId }}</h2>
-  <p
-    v-if="station == null"
-    class="show-details__empty"
-  >
-    Charging station not found
-  </p>
+  <template v-if="station == null">
+    <p class="show-details__empty">
+      Charging station not found
+    </p>
+    <Button
+      id="action-button"
+      @click="close()"
+    >
+      Back to Charging Stations
+    </Button>
+  </template>
   <template v-else>
     <table
       v-for="section in sections"
       </tbody>
     </table>
   </template>
-  <Button
-    id="action-button"
-    @click="close()"
-  >
-    Close
-  </Button>
 </template>
 
 <script setup lang="ts">
@@ -104,12 +103,27 @@ const close = (): void => {
 </script>
 
 <style scoped>
+/* Bound the width: the shared action container is `min-width: max-content`,
+ * so uncapped these tables would grow it to fill the main area. */
 .show-details__section {
-  margin-bottom: var(--spacing-md);
+  width: 32rem;
+  margin-bottom: var(--spacing-lg);
+}
+
+.show-details__section:last-of-type {
+  margin-bottom: 0;
 }
 
 .show-details__section :is(th, td) {
   text-align: left;
+  vertical-align: top;
+  overflow-wrap: anywhere;
+}
+
+.show-details__section th[scope='row'] {
+  font-weight: bold;
+  background-color: var(--color-bg-header);
+  border-right: solid 0.25px var(--color-border);
 }
 
 .show-details__empty {
index d74124b00588b8e741671065fbda9aec4d68bb46..669e7056beda35f915f42917fa578aabc3bb8a47 100644 (file)
@@ -134,12 +134,23 @@ const close = (): void => {
   display: flex;
   flex-direction: column;
   gap: var(--skin-space-2);
+  padding: var(--skin-space-3);
+  background-color: var(--skin-surface-sunken);
+  border: 1px solid var(--skin-border);
+  border-radius: var(--skin-radius-lg);
+}
+
+.station-details__section > h3 {
+  font-size: 0.8125rem;
+  color: var(--color-text-strong);
+  padding-bottom: var(--skin-space-2);
+  border-bottom: 1px solid var(--skin-border);
 }
 
 .station-details__list {
   margin: 0;
   display: grid;
-  gap: var(--skin-space-2);
+  gap: var(--skin-space-1);
 }
 
 .station-details__row {
@@ -149,6 +160,11 @@ const close = (): void => {
   align-items: baseline;
 }
 
+.station-details__row + .station-details__row {
+  padding-top: var(--skin-space-1);
+  border-top: 1px solid var(--skin-border);
+}
+
 .station-details__row dt {
   margin: 0;
   font-size: 0.6875rem;
index 1a2d18f198da0b33b12ee27574ac1c354059a595..9b86caedd18fd24f0192e51681dc599ffe6c9106 100644 (file)
@@ -422,10 +422,6 @@ describe('Actions', () => {
   })
 
   describe('ShowDetails', () => {
-    beforeEach(() => {
-      mockPush.mockClear()
-    })
-
     afterEach(() => {
       vi.clearAllMocks()
       vi.restoreAllMocks()
@@ -506,8 +502,8 @@ describe('Actions', () => {
       expect(wrapper.text()).toContain('Charging station not found')
     })
 
-    it('should navigate to charging-stations on close', async () => {
-      const wrapper = mountShowDetails()
+    it('should navigate to charging-stations from the not-found panel', async () => {
+      const wrapper = mountShowDetails([])
       await wrapper.findComponent(ButtonStub).trigger('click')
       await flushPromises()
       expect(mockPush).toHaveBeenCalledWith({ name: 'charging-stations' })
index bb320a7836a4165dcc7afbac28380bcf0af3666c..512d469c7f6394b1723a33f79507af5d4c25cea3 100644 (file)
@@ -313,5 +313,27 @@ describe('CSData', () => {
       toggleProps.off?.()
       expect(mockPush).toHaveBeenCalledWith({ name: 'charging-stations' })
     })
+
+    it('should trigger router push to show-details on toggle on', () => {
+      const wrapper = mountCSData()
+      const toggleButtons = wrapper.findAllComponents(ToggleButtonStub)
+      expect(toggleButtons[1].props('id')).toBe(`${TEST_HASH_ID}-show-details`)
+      const toggleProps = toggleButtons[1].props() as unknown as StubProps
+      toggleProps.on?.()
+      expect(mockPush).toHaveBeenCalledOnce()
+      const callArg = mockPush.mock.calls[0][0] as { name: string; params: Record<string, string> }
+      expect(callArg.name).toBe('show-details')
+      expect(callArg.params.hashId).toBe(TEST_HASH_ID)
+      expect(callArg.params.chargingStationId).toBe(TEST_STATION_ID)
+    })
+
+    it('should trigger router push to charging-stations on show-details toggle off', () => {
+      const wrapper = mountCSData()
+      const toggleButtons = wrapper.findAllComponents(ToggleButtonStub)
+      expect(toggleButtons[1].props('id')).toBe(`${TEST_HASH_ID}-show-details`)
+      const toggleProps = toggleButtons[1].props() as unknown as StubProps
+      toggleProps.off?.()
+      expect(mockPush).toHaveBeenCalledWith({ name: 'charging-stations' })
+    })
   })
 })