diff --git a/core/http/react-ui/e2e/addon-navigation.spec.js b/core/http/react-ui/e2e/addon-navigation.spec.js index 86435df7b..6454c6135 100644 --- a/core/http/react-ui/e2e/addon-navigation.spec.js +++ b/core/http/react-ui/e2e/addon-navigation.spec.js @@ -26,12 +26,12 @@ async function setup(page, { apps = [], pins = ['studio', 'talk'], addons = true } test.describe('Add-on navigation', () => { - test('the sidebar lists the pinned apps under Apps, in pin order, with Show all last', async ({ page }) => { + test('the sidebar lists the pinned apps under Apps, in pin order, and no Show all link', async ({ page }) => { await setup(page) await page.goto('/app') const nav = page.getByRole('navigation') await expect(nav.getByRole('link', { name: 'Chat' })).toBeVisible() - // Apps, then the pinned apps in pin order, then Show all as the last line. + // Apps, then the pinned apps in pin order; Apps is the only add-on entry. await expect(nav.getByRole('link', { name: 'Apps', exact: true })).toHaveCount(1) const pinned = nav.getByTestId('apps-pinned-link') await expect(pinned).toHaveCount(2) @@ -39,10 +39,11 @@ test.describe('Add-on navigation', () => { await expect(pinned.nth(0)).toHaveText('Studio') await expect(pinned.nth(1)).toHaveAttribute('data-app', 'talk') await expect(pinned.nth(1)).toHaveText('Talk') - await expect(nav.getByTestId('apps-show-all')).toHaveText('Show all') + await expect(nav.getByTestId('apps-show-all')).toHaveCount(0) + await expect(nav.getByText('Show all')).toHaveCount(0) const group = await nav.locator('a.nav-item').evaluateAll(els => els.map(e => e.textContent.trim())) const at = group.indexOf('Apps') - expect(group.slice(at, at + 4)).toEqual(['Apps', 'Studio', 'Talk', 'Show all']) + expect(group.slice(at, at + 4)).toEqual(['Apps', 'Studio', 'Talk']) await expect(pinned.nth(0)).toHaveAttribute('href', /\/app\/addons\/studio\//) // There is no Pinned section. await expect(page.getByTestId('pinned-nav')).toHaveCount(0) @@ -54,11 +55,12 @@ test.describe('Add-on navigation', () => { await page.goto('/app') const nav = page.getByRole('navigation') await expect(nav.getByTestId('apps-pinned-link')).toHaveText(['Talk', 'Studio']) - await expect(nav.getByTestId('apps-show-all')).toBeVisible() + await expect(nav.getByTestId('apps-show-all')).toHaveCount(0) await page.unroute('**/api/addons/pins') await page.route('**/api/addons/pins', route => route.fulfill({ json: [] })) await page.goto('/app') - await expect(nav.getByTestId('apps-show-all')).toBeVisible() + await expect(nav.getByRole('link', { name: 'Apps', exact: true })).toHaveCount(1) + await expect(nav.getByTestId('apps-show-all')).toHaveCount(0) await expect(nav.getByTestId('apps-pinned-link')).toHaveCount(0) }) diff --git a/core/http/react-ui/public/locales/en/nav.json b/core/http/react-ui/public/locales/en/nav.json index c17375216..673111fba 100644 --- a/core/http/react-ui/public/locales/en/nav.json +++ b/core/http/react-ui/public/locales/en/nav.json @@ -66,8 +66,7 @@ "trafficModels": "Models", "gpuHost": "GPU and host", "prometheus": "Prometheus", - "addons": "Apps", - "showAll": "Show all" + "addons": "Apps" }, "footer": { "github": "GitHub", diff --git a/core/http/react-ui/src/App.css b/core/http/react-ui/src/App.css index 502a504e1..8dc2259f5 100644 --- a/core/http/react-ui/src/App.css +++ b/core/http/react-ui/src/App.css @@ -9693,5 +9693,5 @@ button.lane:hover { .lai-icon--beat { animation: none; } } -/* The "Show all" line under the Apps entry: its label lines up with the labels above it. */ +/* Pinned apps under the Apps entry: their labels line up with the labels above them. */ .sidebar-nav .nav-item.nav-item--sub { padding-inline-start: 36px; } diff --git a/core/http/react-ui/src/components/Sidebar.jsx b/core/http/react-ui/src/components/Sidebar.jsx index 7ae71e40e..693a40a72 100644 --- a/core/http/react-ui/src/components/Sidebar.jsx +++ b/core/http/react-ui/src/components/Sidebar.jsx @@ -220,15 +220,8 @@ export default function Sidebar({ isOpen, onClose }) { {(legacy ? visibleTopItems : [...visibleTopItems.filter(i => i.path === '/app'), ...primary]).map(item => ( - {/* The Apps group: the pinned apps in pin order, then the full list - of apps as the last line. Show all has no active style of its - own; the Apps entry above carries it. */} + {/* The Apps group: the pinned apps in pin order, under the Apps entry. */} {!legacy && item.addonsEntry && !collapsed && } - {!legacy && item.addonsEntry && !collapsed && ( - - {t('items.showAll')} - - )} ))} diff --git a/docs/content/features/addons.md b/docs/content/features/addons.md index 300cd723a..fbabe0f99 100644 --- a/docs/content/features/addons.md +++ b/docs/content/features/addons.md @@ -1711,9 +1711,8 @@ reach **Browse**, **Installed** and **Libraries** from the drawer. The tabs are convenience: the owner checks administration again on every call, so a request that skips the page is denied the same way (403). -The sidebar has the items **Chat**, **Models** and **Apps**, and below Apps a **Show all** -link. Both open the add-ons drawer at `/app/addons`. The pinned apps are listed between them, -in pin order, as indented links with the app icon and title; nothing is listed when no app is +The sidebar has the items **Chat**, **Models** and **Apps**. **Apps** opens the add-ons drawer at +`/app/addons`. The pinned apps are listed under it, in pin order, as indented links with the app icon and title; nothing is listed when no app is pinned, and the list is hidden when the sidebar is collapsed. The user pins add-ons and changes their order with the buttons on a pinned tile in the drawer. Pins belong to the user, follow the user across browsers, and are capped at eight. A pin whose add-on is stopped opens the detail view; a pin whose add-on