@7365admin1/layer-common 3.2.2-staging.194 → 3.2.2-staging.195

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.
@@ -88,10 +88,9 @@
88
88
  @update:model-value="
89
89
  toggleCategory(String(resourceKey), $event)
90
90
  "
91
- color="accent"
91
+ class="app-switch"
92
92
  density="compact"
93
93
  hide-details
94
- inset
95
94
  />
96
95
  </template>
97
96
  </v-list-item>
@@ -47,10 +47,9 @@
47
47
  <v-switch
48
48
  :model-value="isCategoryEnabled(String(resourceKey))"
49
49
  :disabled="!edit"
50
- color="accent"
50
+ class="app-switch"
51
51
  density="compact"
52
52
  hide-details
53
- inset
54
53
  @update:model-value="
55
54
  toggleCategory(String(resourceKey), $event)
56
55
  "
@@ -84,6 +84,7 @@
84
84
  :permissions="props.permissions"
85
85
  :org="props.orgId"
86
86
  :site-id="props.siteId"
87
+ :site-state="siteFieldShown"
87
88
  :web-only-resources="props.webOnlyResources"
88
89
  @success="success()"
89
90
  :type="props.type"
@@ -184,6 +185,9 @@
184
185
  </template>
185
186
 
186
187
  <script setup lang="ts">
188
+ // Relative import: this layer's `utils/` is not auto-imported into consumers.
189
+ import { showSiteField } from "../utils/role";
190
+
187
191
  const props = defineProps({
188
192
  orgId: {
189
193
  type: String,
@@ -197,6 +201,18 @@ const props = defineProps({
197
201
  type: String,
198
202
  default: "",
199
203
  },
204
+ /*
205
+ * Whether the Create-role dialog shows its (read-only) Site field.
206
+ * The dialog defaulted this to `true` but this component never declared or
207
+ * forwarded it, and its root is a `<div>`, so a fallthrough `site-state`
208
+ * attribute landed on that div instead of the dialog - no consuming app
209
+ * could switch the field off. Admin roles are never site-scoped, so the
210
+ * field drew permanently disabled and empty there (QA, admin PR #18).
211
+ */
212
+ siteState: {
213
+ type: Boolean,
214
+ default: true,
215
+ },
200
216
  type: {
201
217
  type: String,
202
218
  required: true,
@@ -296,6 +312,10 @@ function tableRowClickHandler(_: any, data: any) {
296
312
 
297
313
  const createDialog = ref(false);
298
314
 
315
+ const siteFieldShown = computed(() =>
316
+ showSiteField(props.siteState, props.type)
317
+ );
318
+
299
319
  function success() {
300
320
  createDialog.value = false;
301
321
  getRoles();
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@7365admin1/layer-common",
3
3
  "license": "MIT",
4
4
  "type": "module",
5
- "version": "3.2.2-staging.194",
5
+ "version": "3.2.2-staging.195",
6
6
  "author": "7365admin1",
7
7
  "main": "./nuxt.config.ts",
8
8
  "//files": "What a consumer extending this layer actually loads. Without this npm ships the whole working tree - the changesets, the CI workflows, the render harness in tools/ and any scratch directory that happened to exist at publish time. Nuxt resolves a layer by directory, so every runtime directory below has to stay listed; adding a new top-level runtime directory means adding it here too.",
@@ -0,0 +1,69 @@
1
+ import assert from "node:assert/strict";
2
+ import { readFileSync } from "node:fs";
3
+ import { test } from "node:test";
4
+
5
+ import { showSiteField } from "./role.ts";
6
+
7
+ /**
8
+ * QA on the admin app (PR #18) reported "no site options" on Create Role. The
9
+ * Site field is read-only by design - it shows the site a role belongs to -
10
+ * but an admin role has no site, so it drew permanently disabled and empty.
11
+ * `RolePermissionFormCreate` defaulted `siteState` to `true` and
12
+ * `RolePermissionMain` neither declared nor forwarded it, and its root is a
13
+ * `<div>`, so a fallthrough attribute could not reach the dialog either: no
14
+ * consuming app could switch the field off.
15
+ */
16
+ test("an admin role never shows the site field", () => {
17
+ assert.equal(showSiteField(true, "admin"), false);
18
+ assert.equal(showSiteField(false, "admin"), false);
19
+ });
20
+
21
+ test("every other role type keeps what the app asked for", () => {
22
+ for (const type of [
23
+ "app",
24
+ "organization",
25
+ "security_agency",
26
+ "cleaning_services",
27
+ ]) {
28
+ assert.equal(showSiteField(true, type), true, type);
29
+ assert.equal(showSiteField(false, type), false, type);
30
+ }
31
+ });
32
+
33
+ test("RolePermissionMain declares site-state and forwards it to the dialog", () => {
34
+ const src = readFileSync(
35
+ new URL("../components/RolePermissionMain.vue", import.meta.url),
36
+ "utf8"
37
+ );
38
+
39
+ assert.match(src, /siteState:\s*\{/, "site-state must be a declared prop");
40
+ assert.match(
41
+ src,
42
+ /:site-state="siteFieldShown"/,
43
+ "the dialog must receive the resolved value, not a fallthrough attribute"
44
+ );
45
+ });
46
+
47
+ /**
48
+ * The category expand toggle carried no class, so it fell back to raw Vuetify
49
+ * `inset` styling: a white thumb on a white card, measured 1.02:1 light /
50
+ * 1.04:1 dark against the dialog surface. `.app-switch` (primitives.css) is
51
+ * this layer's switch shape and is what makes the thumb visible. `inset` is
52
+ * removed with it - Vuetify's inset dimensions beat the `.app-switch` rules on
53
+ * source order and collapse the control to zero width.
54
+ */
55
+ for (const file of [
56
+ "RolePermissionFormCreate.vue",
57
+ "RolePermissionFormPreviewUpdate.vue",
58
+ ]) {
59
+ test(`${file} styles its category toggle with .app-switch`, () => {
60
+ const src = readFileSync(
61
+ new URL(`../components/${file}`, import.meta.url),
62
+ "utf8"
63
+ );
64
+ const sw = src.slice(src.indexOf("<v-switch"), src.indexOf("/>", src.indexOf("<v-switch")));
65
+
66
+ assert.ok(sw.includes('class="app-switch"'), "missing .app-switch");
67
+ assert.ok(!/\binset\b/.test(sw), "inset fights the .app-switch dimensions");
68
+ });
69
+ }
package/utils/role.ts ADDED
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Whether the Create-role dialog should show its (read-only) Site field.
3
+ *
4
+ * An admin role is org-wide, never site-scoped, so there is no site to show -
5
+ * the field could only ever render empty and permanently disabled, which is
6
+ * what QA reported on the admin app. Every other role type keeps whatever the
7
+ * consuming app asked for.
8
+ */
9
+ export const showSiteField = (siteState: boolean, type: string): boolean =>
10
+ siteState && type !== "admin";