@igstack/app-catalog-frontend-core 0.9.0 → 0.9.1
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.
- package/dist/esm/modules/appCatalog/ui/components/ViewToggle.d.ts +7 -1
- package/dist/esm/modules/appCatalog/ui/components/ViewToggle.js +8 -6
- package/dist/esm/modules/appCatalog/ui/components/ViewToggle.js.map +1 -1
- package/package.json +4 -4
- package/src/__tests__/integration/serviceDesks.integration.test.ts +36 -0
- package/src/modules/appCatalog/ui/components/ViewToggle.tsx +17 -7
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Compact segmented toggle for the header (Apps | Service Desks). Rendered in
|
|
3
3
|
* the header's `middle` slot so it adds no header height. Uses router Links so
|
|
4
|
-
*
|
|
4
|
+
* each segment is deep-linkable.
|
|
5
|
+
*
|
|
6
|
+
* The active segment is derived from the current pathname rather than a plain
|
|
7
|
+
* exact-match on the Link: the "Apps" tab must stay active on the app-detail
|
|
8
|
+
* routes (`/app/<slug>`) too, since the detail panel is part of the Apps view
|
|
9
|
+
* (#23). A plain `activeOptions={{ exact: true }}` on `to="/"` left both tabs
|
|
10
|
+
* inactive on `/app/<slug>`.
|
|
5
11
|
*/
|
|
6
12
|
export declare function ViewToggle(): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { jsxs, jsx } from "react/jsx-runtime";
|
|
2
|
-
import { Link } from "@tanstack/react-router";
|
|
2
|
+
import { useRouterState, Link } from "@tanstack/react-router";
|
|
3
3
|
import { cn } from "../../../../lib/utils.js";
|
|
4
4
|
function ViewToggle() {
|
|
5
|
+
const pathname = useRouterState({ select: (s) => s.location.pathname });
|
|
6
|
+
const appsActive = pathname === "/" || pathname.startsWith("/app/");
|
|
7
|
+
const desksActive = pathname.startsWith("/service-desks");
|
|
5
8
|
const segment = "inline-flex items-center rounded-md px-3 py-1 text-sm font-medium transition-colors";
|
|
6
9
|
const active = "bg-background text-foreground shadow-sm";
|
|
7
10
|
const inactive = "text-muted-foreground hover:text-foreground";
|
|
@@ -17,9 +20,8 @@ function ViewToggle() {
|
|
|
17
20
|
{
|
|
18
21
|
to: "/",
|
|
19
22
|
"aria-label": "Apps",
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
activeProps: { className: cn(segment, active) },
|
|
23
|
+
"aria-current": appsActive ? "page" : void 0,
|
|
24
|
+
className: cn(segment, appsActive ? active : inactive),
|
|
23
25
|
children: "Apps"
|
|
24
26
|
}
|
|
25
27
|
),
|
|
@@ -28,8 +30,8 @@ function ViewToggle() {
|
|
|
28
30
|
{
|
|
29
31
|
to: "/service-desks",
|
|
30
32
|
"aria-label": "Service Desks",
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
"aria-current": desksActive ? "page" : void 0,
|
|
34
|
+
className: cn(segment, desksActive ? active : inactive),
|
|
33
35
|
children: "Service Desks"
|
|
34
36
|
}
|
|
35
37
|
)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ViewToggle.js","sources":["../../../../../../src/modules/appCatalog/ui/components/ViewToggle.tsx"],"sourcesContent":["import { Link } from '@tanstack/react-router'\nimport { cn } from '~/lib/utils'\n\n/**\n * Compact segmented toggle for the header (Apps | Service Desks). Rendered in\n * the header's `middle` slot so it adds no header height. Uses router Links so\n *
|
|
1
|
+
{"version":3,"file":"ViewToggle.js","sources":["../../../../../../src/modules/appCatalog/ui/components/ViewToggle.tsx"],"sourcesContent":["import { Link, useRouterState } from '@tanstack/react-router'\nimport { cn } from '~/lib/utils'\n\n/**\n * Compact segmented toggle for the header (Apps | Service Desks). Rendered in\n * the header's `middle` slot so it adds no header height. Uses router Links so\n * each segment is deep-linkable.\n *\n * The active segment is derived from the current pathname rather than a plain\n * exact-match on the Link: the \"Apps\" tab must stay active on the app-detail\n * routes (`/app/<slug>`) too, since the detail panel is part of the Apps view\n * (#23). A plain `activeOptions={{ exact: true }}` on `to=\"/\"` left both tabs\n * inactive on `/app/<slug>`.\n */\nexport function ViewToggle() {\n const pathname = useRouterState({ select: (s) => s.location.pathname })\n\n const appsActive = pathname === '/' || pathname.startsWith('/app/')\n const desksActive = pathname.startsWith('/service-desks')\n\n const segment =\n 'inline-flex items-center rounded-md px-3 py-1 text-sm font-medium transition-colors'\n const active = 'bg-background text-foreground shadow-sm'\n const inactive = 'text-muted-foreground hover:text-foreground'\n\n return (\n <div\n role=\"tablist\"\n aria-label=\"View\"\n className=\"inline-flex items-center gap-1 rounded-lg bg-muted p-[3px]\"\n >\n <Link\n to=\"/\"\n aria-label=\"Apps\"\n aria-current={appsActive ? 'page' : undefined}\n className={cn(segment, appsActive ? active : inactive)}\n >\n Apps\n </Link>\n <Link\n to=\"/service-desks\"\n aria-label=\"Service Desks\"\n aria-current={desksActive ? 'page' : undefined}\n className={cn(segment, desksActive ? active : inactive)}\n >\n Service Desks\n </Link>\n </div>\n )\n}\n"],"names":[],"mappings":";;;AAcO,SAAS,aAAa;AAC3B,QAAM,WAAW,eAAe,EAAE,QAAQ,CAAC,MAAM,EAAE,SAAS,UAAU;AAEtE,QAAM,aAAa,aAAa,OAAO,SAAS,WAAW,OAAO;AAClE,QAAM,cAAc,SAAS,WAAW,gBAAgB;AAExD,QAAM,UACJ;AACF,QAAM,SAAS;AACf,QAAM,WAAW;AAEjB,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,MAAK;AAAA,MACL,cAAW;AAAA,MACX,WAAU;AAAA,MAEV,UAAA;AAAA,QAAA;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,IAAG;AAAA,YACH,cAAW;AAAA,YACX,gBAAc,aAAa,SAAS;AAAA,YACpC,WAAW,GAAG,SAAS,aAAa,SAAS,QAAQ;AAAA,YACtD,UAAA;AAAA,UAAA;AAAA,QAAA;AAAA,QAGD;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,IAAG;AAAA,YACH,cAAW;AAAA,YACX,gBAAc,cAAc,SAAS;AAAA,YACrC,WAAW,GAAG,SAAS,cAAc,SAAS,QAAQ;AAAA,YACvD,UAAA;AAAA,UAAA;AAAA,QAAA;AAAA,MAED;AAAA,IAAA;AAAA,EAAA;AAGN;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@igstack/app-catalog-frontend-core",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.1",
|
|
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.9.
|
|
138
|
-
"@igstack/app-catalog-shared-core": "0.9.
|
|
137
|
+
"@igstack/app-catalog-backend-core": "0.9.1",
|
|
138
|
+
"@igstack/app-catalog-shared-core": "0.9.1"
|
|
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": "
|
|
147
|
+
"gitHead": "69c9cf1a9802f07ead88d1d97496cbabaec5ca37",
|
|
148
148
|
"scripts": {
|
|
149
149
|
"build": "vite build",
|
|
150
150
|
"build:lenient": "vite build --mode lenient",
|
|
@@ -123,3 +123,39 @@ describe('Service Desks view (#9)', () => {
|
|
|
123
123
|
await waitFor(() => expect(search).toHaveFocus())
|
|
124
124
|
})
|
|
125
125
|
})
|
|
126
|
+
|
|
127
|
+
// #23: the header "Apps" tab must stay active while viewing an app-detail route
|
|
128
|
+
// (/app/<slug>) — the detail panel is part of the Apps view. Previously the Apps
|
|
129
|
+
// link was active only on the exact "/" route, so /app/quicksight highlighted
|
|
130
|
+
// neither tab.
|
|
131
|
+
describe('ViewToggle active tab (#23)', () => {
|
|
132
|
+
const appsTab = () => screen.getByRole('link', { name: 'Apps' })
|
|
133
|
+
const deskTab = () => screen.getByRole('link', { name: 'Service Desks' })
|
|
134
|
+
|
|
135
|
+
it('activates the Apps tab on an app-detail route (/app/$slug)', async () => {
|
|
136
|
+
await given(magazine.full(), { initialRoute: '/app/jira' })
|
|
137
|
+
|
|
138
|
+
await waitFor(() =>
|
|
139
|
+
expect(appsTab()).toHaveAttribute('aria-current', 'page'),
|
|
140
|
+
)
|
|
141
|
+
expect(deskTab()).not.toHaveAttribute('aria-current')
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
it('activates the Apps tab on the root route', async () => {
|
|
145
|
+
await given(magazine.full(), { initialRoute: '/' })
|
|
146
|
+
|
|
147
|
+
await waitFor(() =>
|
|
148
|
+
expect(appsTab()).toHaveAttribute('aria-current', 'page'),
|
|
149
|
+
)
|
|
150
|
+
expect(deskTab()).not.toHaveAttribute('aria-current')
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
it('activates only Service Desks on the /service-desks route', async () => {
|
|
154
|
+
await given(magazine.full(), { initialRoute: '/service-desks' })
|
|
155
|
+
|
|
156
|
+
await waitFor(() =>
|
|
157
|
+
expect(deskTab()).toHaveAttribute('aria-current', 'page'),
|
|
158
|
+
)
|
|
159
|
+
expect(appsTab()).not.toHaveAttribute('aria-current')
|
|
160
|
+
})
|
|
161
|
+
})
|
|
@@ -1,12 +1,23 @@
|
|
|
1
|
-
import { Link } from '@tanstack/react-router'
|
|
1
|
+
import { Link, useRouterState } from '@tanstack/react-router'
|
|
2
2
|
import { cn } from '~/lib/utils'
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Compact segmented toggle for the header (Apps | Service Desks). Rendered in
|
|
6
6
|
* the header's `middle` slot so it adds no header height. Uses router Links so
|
|
7
|
-
*
|
|
7
|
+
* each segment is deep-linkable.
|
|
8
|
+
*
|
|
9
|
+
* The active segment is derived from the current pathname rather than a plain
|
|
10
|
+
* exact-match on the Link: the "Apps" tab must stay active on the app-detail
|
|
11
|
+
* routes (`/app/<slug>`) too, since the detail panel is part of the Apps view
|
|
12
|
+
* (#23). A plain `activeOptions={{ exact: true }}` on `to="/"` left both tabs
|
|
13
|
+
* inactive on `/app/<slug>`.
|
|
8
14
|
*/
|
|
9
15
|
export function ViewToggle() {
|
|
16
|
+
const pathname = useRouterState({ select: (s) => s.location.pathname })
|
|
17
|
+
|
|
18
|
+
const appsActive = pathname === '/' || pathname.startsWith('/app/')
|
|
19
|
+
const desksActive = pathname.startsWith('/service-desks')
|
|
20
|
+
|
|
10
21
|
const segment =
|
|
11
22
|
'inline-flex items-center rounded-md px-3 py-1 text-sm font-medium transition-colors'
|
|
12
23
|
const active = 'bg-background text-foreground shadow-sm'
|
|
@@ -21,17 +32,16 @@ export function ViewToggle() {
|
|
|
21
32
|
<Link
|
|
22
33
|
to="/"
|
|
23
34
|
aria-label="Apps"
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
activeProps={{ className: cn(segment, active) }}
|
|
35
|
+
aria-current={appsActive ? 'page' : undefined}
|
|
36
|
+
className={cn(segment, appsActive ? active : inactive)}
|
|
27
37
|
>
|
|
28
38
|
Apps
|
|
29
39
|
</Link>
|
|
30
40
|
<Link
|
|
31
41
|
to="/service-desks"
|
|
32
42
|
aria-label="Service Desks"
|
|
33
|
-
|
|
34
|
-
|
|
43
|
+
aria-current={desksActive ? 'page' : undefined}
|
|
44
|
+
className={cn(segment, desksActive ? active : inactive)}
|
|
35
45
|
>
|
|
36
46
|
Service Desks
|
|
37
47
|
</Link>
|