@igstack/app-catalog-frontend-core 0.15.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -41,11 +41,6 @@ export declare class AppDetailTools {
41
41
  clickViewReplacement(): Promise<void>;
42
42
  /** The title shown in the currently open detail panel (empty if none). */
43
43
  getOpenTitle(): string;
44
- /**
45
- * The prominent primary "Open" button in the app detail card.
46
- * Returns the anchor element when the app has an appUrl, null otherwise.
47
- */
48
- getOpenButton(): HTMLAnchorElement | null;
49
44
  private getPanel;
50
45
  private findHeading;
51
46
  private getNextSiblingText;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@igstack/app-catalog-frontend-core",
3
- "version": "0.15.0",
3
+ "version": "0.16.0",
4
4
  "description": "Frontend core library for App Catalog",
5
5
  "homepage": "https://github.com/lislon/app-catalog",
6
6
  "repository": {
@@ -134,8 +134,8 @@
134
134
  "vite-plugin-static-copy": "^3.1.4",
135
135
  "vite-plugin-svgr": "^4.2.0",
136
136
  "vitest": "^4.1.2",
137
- "@igstack/app-catalog-backend-core": "0.15.0",
138
- "@igstack/app-catalog-shared-core": "0.15.0"
137
+ "@igstack/app-catalog-backend-core": "0.16.0",
138
+ "@igstack/app-catalog-shared-core": "0.16.0"
139
139
  },
140
140
  "peerDependencies": {
141
141
  "react": "19.1.2",
@@ -144,7 +144,7 @@
144
144
  "vite": "^6.3.5",
145
145
  "vite-plugin-svgr": "^4.2.0"
146
146
  },
147
- "gitHead": "e021e223f8da284a81e2204908587d55b2f87b6a",
147
+ "gitHead": "8ef46d75c527a6d4a00965f7529e000c03cf0bc6",
148
148
  "scripts": {
149
149
  "build": "vite build",
150
150
  "build:lenient": "vite build --mode lenient",
@@ -148,36 +148,6 @@ describe('App Catalog Integration', () => {
148
148
  expect(ui.catalog.isDetailPanelOpen()).toBe(false)
149
149
  })
150
150
 
151
- // Detail card UX (#45): the detail panel shows a prominent primary "Open" button
152
- // with the destination URL visible alongside it.
153
- it('shows a prominent primary Open button with URL in the app detail card', async () => {
154
- const { ui } = await given(
155
- magazine.custom(({ backendCfg }) => {
156
- backendCfg.withApp({
157
- displayName: 'Website App',
158
- description: 'Has a website',
159
- appUrl: 'https://example.natera.com/app',
160
- })
161
- backendCfg.withApp({
162
- displayName: 'No URL App',
163
- description: 'No url',
164
- })
165
- }),
166
- )
167
-
168
- await ui.catalog.openApp('Website App')
169
- const btn = ui.app.getOpenButton()
170
- expect(btn).not.toBeNull()
171
- expect(btn!.getAttribute('href')).toBe('https://example.natera.com/app')
172
- expect(btn!.getAttribute('target')).toBe('_blank')
173
- // The URL is shown in the button (stripped of protocol).
174
- expect(btn!.textContent).toContain('example.natera.com/app')
175
-
176
- // An app without appUrl shows no Open button.
177
- await ui.catalog.openApp('No URL App')
178
- expect(ui.app.getOpenButton()).toBeNull()
179
- })
180
-
181
151
  // Test 5: Returning user — cached data + no onboarding + backend down
182
152
  it('returning user sees cached apps even when backend is unavailable', async () => {
183
153
  suppressConsole([/TRPC Error/, /Failed to fetch/])
@@ -328,47 +298,4 @@ describe('App Catalog Integration', () => {
328
298
  /(?<![\d])0(?![\d]).*clear filters/i,
329
299
  )
330
300
  })
331
-
332
- it('highlights matched query text in search result app names (#57)', async () => {
333
- const { ui } = await given(
334
- magazine.custom(({ backendCfg }) => {
335
- backendCfg.withApp({ displayName: 'Kubernetes Platform' })
336
- backendCfg.withApp({ displayName: 'Other Tool' })
337
- }),
338
- )
339
-
340
- await ui.catalog.search('kube')
341
-
342
- await waitFor(() => {
343
- const marks = document.querySelectorAll('mark')
344
- const markTexts = [...marks].map((m) => String(m.textContent))
345
- expect(markTexts.some((t) => t.toLowerCase().includes('kube'))).toBe(true)
346
- })
347
- })
348
-
349
- it('clear (×) button appears when search has text and clears on click (#54)', async () => {
350
- const { ui } = await given(magazine.full())
351
-
352
- // Before typing: no clear button
353
- expect(
354
- document.querySelector('[aria-label="Clear search"]'),
355
- ).not.toBeInTheDocument()
356
-
357
- // After typing: clear button appears
358
- await ui.catalog.search('jira')
359
- await waitFor(() => {
360
- expect(
361
- document.querySelector('[aria-label="Clear search"]'),
362
- ).toBeInTheDocument()
363
- })
364
-
365
- // Click × — input clears, button disappears
366
- fireEvent.click(document.querySelector('[aria-label="Clear search"]')!)
367
- await waitFor(() => {
368
- expect(ui.catalog.getSearchInput().value).toBe('')
369
- expect(
370
- document.querySelector('[aria-label="Clear search"]'),
371
- ).not.toBeInTheDocument()
372
- })
373
- })
374
301
  })
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, it } from 'vitest'
2
- import { fireEvent, screen, waitFor } from '@testing-library/react'
2
+ import { fireEvent, waitFor } from '@testing-library/react'
3
3
  import '@testing-library/jest-dom/vitest'
4
4
 
5
5
  import { given } from './harness/given'
@@ -45,29 +45,6 @@ describe('App deep-link routing', () => {
45
45
  expect(router.state.location.pathname).toBe('/')
46
46
  })
47
47
 
48
- it('Esc closes card opened by mouse click (#53 bug 1)', async () => {
49
- const { ui, router } = await given(magazine.full(), { initialRoute: '/' })
50
- // Open via click (mouse, not keyboard nav)
51
- await ui.catalog.openApp('Jira')
52
- await waitFor(() => expect(ui.catalog.isDetailPanelOpen()).toBe(true))
53
- // Esc should close — no "dead click" needed
54
- fireEvent.keyDown(document, { key: 'Escape', code: 'Escape' })
55
- await waitFor(() => expect(ui.catalog.isDetailPanelOpen()).toBe(false))
56
- expect(router.state.location.pathname).toBe('/')
57
- })
58
-
59
- it('closes the detail panel when the × close button is clicked (#59)', async () => {
60
- const { ui, router } = await given(magazine.full(), { initialRoute: '/' })
61
- await ui.catalog.openApp('Jira')
62
- await waitFor(() => expect(ui.catalog.isDetailPanelOpen()).toBe(true))
63
- const closeBtn = screen.getByRole('button', { name: /^Close$/i })
64
- fireEvent.click(closeBtn)
65
- await waitFor(() => {
66
- expect(ui.catalog.isDetailPanelOpen()).toBe(false)
67
- })
68
- expect(router.state.location.pathname).toBe('/')
69
- })
70
-
71
48
  it('ignores legacy ?app= query param (no detail opens)', async () => {
72
49
  const { ui } = await given(magazine.full(), { initialRoute: '/?app=jira' })
73
50
  await waitFor(() => {
@@ -45,21 +45,6 @@ describe('App freshness line', () => {
45
45
  expect(screen.getByText(/may be out of date/i)).toBeInTheDocument()
46
46
  })
47
47
 
48
- it('shows "Added" date before Sources when createdAt is present (#55)', async () => {
49
- const { ui } = await given(
50
- magazine.full(({ backendCfg }) => {
51
- backendCfg.withApp({
52
- slug: 'dated-app',
53
- displayName: 'Dated App',
54
- createdAt: iso(30 * DAY),
55
- })
56
- }),
57
- { initialRoute: '/app/dated-app' },
58
- )
59
- await waitFor(() => expect(ui.catalog.isDetailPanelOpen()).toBe(true))
60
- expect(screen.getByText(/Added/i)).toBeInTheDocument()
61
- })
62
-
63
48
  it('shows no freshness line when the app was never scanned', async () => {
64
49
  const { ui } = await given(
65
50
  magazine.full(({ backendCfg }) => {
@@ -152,17 +152,6 @@ export class AppDetailTools {
152
152
  return panel.querySelector('.text-2xl')?.textContent.trim() ?? ''
153
153
  }
154
154
 
155
- /**
156
- * The prominent primary "Open" button in the app detail card.
157
- * Returns the anchor element when the app has an appUrl, null otherwise.
158
- */
159
- getOpenButton(): HTMLAnchorElement | null {
160
- const panel = this.getPanel()
161
- return (
162
- panel.querySelector<HTMLAnchorElement>('[aria-label^="Open "]') ?? null
163
- )
164
- }
165
-
166
155
  private getPanel(): HTMLElement {
167
156
  const closeButton = screen.queryByLabelText('Close details panel')
168
157
  if (!closeButton) {