Skip to content

Commit a9a9905

Browse files
authored
feat: tag-scoped property choices (#12295)
* feat: tag-scoped property choices * Able to hide global choices per tag * add e2e tests
1 parent fd7e685 commit a9a9905

9 files changed

Lines changed: 255 additions & 39 deletions

File tree

clj-e2e/deps.edn

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
:deps {org.clojure/clojure {:mvn/version "1.12.0"}
33
;; io.github.pfeodrippe/wally {:local/root "../../../wally"}
44
io.github.pfeodrippe/wally {:git/url "https://github.com/logseq/wally"
5-
:sha "8571fae7c51400ac61c8b1026cbfba68279bc461"}
5+
:sha "8571fae7c51400ac61c8b1026cbfba68279bc461"
6+
:exclusions [com.microsoft.playwright/playwright]}
7+
com.microsoft.playwright/playwright {:mvn/version "1.57.0"}
68
;; io.github.zmedelis/bosquet {:mvn/version "2025.03.28"}
79
org.clj-commons/claypoole {:mvn/version "1.2.2"}
810
metosin/jsonista {:mvn/version "0.3.13"}

clj-e2e/dev/user.clj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
[logseq.e2e.outliner-basic-test]
1414
[logseq.e2e.plugins-basic-test]
1515
[logseq.e2e.property-basic-test]
16+
[logseq.e2e.property-scoped-choices-test]
1617
[logseq.e2e.reference-basic-test]
1718
[logseq.e2e.rtc-basic-test]
1819
[logseq.e2e.rtc-extra-part2-test]
@@ -45,6 +46,11 @@
4546
(->> (future (run-tests 'logseq.e2e.property-basic-test))
4647
(swap! *futures assoc :property-test)))
4748

49+
(defn run-property-scoped-choices-test
50+
[]
51+
(->> (future (run-tests 'logseq.e2e.property-scoped-choices-test))
52+
(swap! *futures assoc :property-scoped-choices-test)))
53+
4854
(defn run-outliner-test
4955
[]
5056
(->> (future (run-tests 'logseq.e2e.outliner-basic-test))
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
(ns logseq.e2e.property-scoped-choices-test
2+
(:require [clojure.test :refer [deftest use-fixtures]]
3+
[logseq.e2e.assert :as assert]
4+
[logseq.e2e.block :as b]
5+
[logseq.e2e.fixtures :as fixtures]
6+
[logseq.e2e.keyboard :as k]
7+
[logseq.e2e.locator :as loc]
8+
[logseq.e2e.page :as page]
9+
[logseq.e2e.util :as util]
10+
[wally.main :as w]
11+
[wally.repl :as repl]))
12+
13+
(use-fixtures :once fixtures/open-page)
14+
15+
(use-fixtures :each
16+
fixtures/new-logseq-page
17+
fixtures/validate-graph)
18+
19+
(defn- add-property
20+
[property-name]
21+
(b/new-blocks ["setup"])
22+
(w/click (util/get-by-text "setup" true))
23+
(k/press "Control+e")
24+
(util/input-command "Add new property")
25+
(w/click "input[placeholder]")
26+
(util/input property-name)
27+
(w/click (w/get-by-text "New option:"))
28+
(w/click (loc/and "span" (util/get-by-text "Text" true)))
29+
(k/esc)
30+
(assert/assert-is-visible (format ".property-k:text('%s')" property-name)))
31+
32+
(defn- add-tag-property
33+
[property-name]
34+
(w/click "button:has-text('Add tag property')")
35+
(w/click "input[placeholder='Add or change property']")
36+
(util/input property-name)
37+
(w/click (loc/filter "a.menu-link" :has-text property-name))
38+
(assert/assert-is-visible (format ".property-k:text('%s')" property-name)))
39+
40+
(defn- open-choices-pane
41+
[property-name]
42+
(w/click (loc/filter ".property-k" :has-text property-name))
43+
(w/click (loc/filter "div[role='menuitem']" :has-text "Available choices")))
44+
45+
(defn- add-choice
46+
[property-name choice]
47+
(open-choices-pane property-name)
48+
(w/click (loc/filter "div[role='menuitem']" :has-text "Add choice"))
49+
(w/fill "input[placeholder='title']" choice)
50+
(w/click "button:has-text('Save')")
51+
(k/esc))
52+
53+
(defn- hide-choice-for-tag
54+
[property-name choice tag]
55+
(open-choices-pane property-name)
56+
(util/wait-timeout 100)
57+
(w/click (format ".choices-list li:has-text('%s') button[title='More settings']" choice))
58+
(util/wait-timeout 100)
59+
(w/click (loc/filter "div[role='menuitem']" :has-text (str "Hide for #" tag)))
60+
(k/esc))
61+
62+
(defn- open-property-value-select
63+
[property-name]
64+
(w/click "div.jtrigger span:has-text('Empty')")
65+
(assert/assert-is-visible (format "input[placeholder='Set %s']" property-name))
66+
(w/click (format "input[placeholder='Set %s']" property-name))
67+
(assert/assert-is-visible ".cp__select-results"))
68+
69+
(deftest tag-scoped-property-choices-test
70+
(let [tag "Device"
71+
property-name "device-type"
72+
scoped-choice "wired"
73+
global-choice "wireless"]
74+
(add-property property-name)
75+
(page/new-page tag)
76+
(page/convert-to-tag tag)
77+
(add-tag-property property-name)
78+
(add-choice property-name scoped-choice)
79+
(util/wait-timeout 100)
80+
(k/esc)
81+
(page/goto-page property-name)
82+
(add-choice property-name global-choice)
83+
(util/wait-timeout 100)
84+
(k/esc)
85+
(page/goto-page tag)
86+
;; open tag properties
87+
(w/click (.first (w/-query "a.block-control")))
88+
(hide-choice-for-tag property-name global-choice tag)
89+
(util/wait-timeout 100)
90+
(k/esc)
91+
(page/new-page "scoped-choices-test")
92+
(b/new-block "Device item")
93+
(util/set-tag tag)
94+
(open-property-value-select property-name)
95+
(assert/assert-is-visible (loc/filter ".cp__select-results" :has-text scoped-choice))
96+
(assert/assert-have-count (loc/filter ".cp__select-results" :has-text global-choice) 0)))

deps/db/src/logseq/db/frontend/property.cljs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,24 @@
275275
:schema {:type :checkbox
276276
:hide? true}
277277
:queryable? false}
278+
;; tag-scoped choice, a choice can be specified locally for specified tags
279+
:logseq.property/choice-classes
280+
{:title "Choice classes"
281+
:schema {:type :class
282+
:cardinality :many
283+
:public? false
284+
:hide? true
285+
:view-context :never}
286+
:queryable? false}
287+
;; tag can define which global choices are hidden for its objects
288+
:logseq.property/choice-exclusions
289+
{:title "Choice exclusions"
290+
:schema {:type :node
291+
:cardinality :many
292+
:public? false
293+
:hide? true
294+
:view-context :never}
295+
:queryable? false}
278296
:logseq.property/checkbox-display-properties
279297
{:title "Properties displayed as checkbox"
280298
:schema {:type :property

deps/db/src/logseq/db/frontend/schema.cljs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
(map (juxt :major :minor)
3838
[(parse-schema-version x) (parse-schema-version y)])))
3939

40-
(def version (parse-schema-version "65.18"))
40+
(def version (parse-schema-version "65.19"))
4141

4242
(defn major-version
4343
"Return a number.

deps/outliner/src/logseq/outliner/property.cljs

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,17 @@
526526
:else
527527
(batch-remove-property! conn [eid] property-id)))))
528528

529+
(defn- set-block-db-attribute!
530+
[conn db block property property-id v]
531+
(throw-error-if-invalid-property-value db property v)
532+
(when-not (and (= property-id :block/alias) (= v (:db/id block))) ; alias can't be itself
533+
(let [tx-data (cond->
534+
[{:db/id (:db/id block) property-id v}]
535+
(= property-id :logseq.property.class/extends)
536+
(conj [:db/retract (:db/id block) :logseq.property.class/extends :logseq.class/Root]))]
537+
(ldb/transact! conn tx-data
538+
{:outliner-op :save-block}))))
539+
529540
(defn set-block-property!
530541
"Updates a block property's value for an existing property-id and block. If
531542
property is a ref type, automatically handles a raw property value i.e. you
@@ -559,15 +570,8 @@
559570
(outliner-validate/validate-extends-property @conn v' [block]))
560571
(cond
561572
db-attribute?
562-
(do
563-
(throw-error-if-invalid-property-value db property v')
564-
(when-not (and (= property-id :block/alias) (= v' (:db/id block))) ; alias can't be itself
565-
(let [tx-data (cond->
566-
[{:db/id (:db/id block) property-id v'}]
567-
(= property-id :logseq.property.class/extends)
568-
(conj [:db/retract (:db/id block) :logseq.property.class/extends :logseq.class/Root]))]
569-
(ldb/transact! conn tx-data
570-
{:outliner-op :save-block}))))
573+
(set-block-db-attribute! conn db block property property-id v)
574+
571575
:else
572576
(let [_ (assert (some? property) (str "Property " property-id " doesn't exist yet"))
573577
ref? (db-property-type/all-ref-property-types property-type)
@@ -580,6 +584,7 @@
580584
(= existing-value v'))]
581585
(throw-error-if-self-value block v' ref?)
582586

587+
(prn :debug :value-matches? value-matches?)
583588
(when-not value-matches?
584589
(raw-set-block-property! conn block property v'))))))))
585590

@@ -729,7 +734,7 @@
729734
(ldb/sort-by-order))))
730735

731736
(defn- build-closed-value-tx
732-
[db property resolved-value {:keys [id icon]}]
737+
[db property resolved-value {:keys [id icon scoped-class-id]}]
733738
(let [block (when id (d/entity db [:block/uuid id]))
734739
block-id (or id (ldb/new-block-id))
735740
icon (when-not (and (string? icon) (string/blank? icon)) icon)
@@ -754,11 +759,13 @@
754759
tx-data' (if (and (:db/id block) (nil? icon))
755760
(conj tx-data [:db/retract (:db/id block) :logseq.property/icon])
756761
tx-data)]
757-
tx-data'))
762+
(cond-> (vec tx-data')
763+
scoped-class-id
764+
(conj [:db/add [:block/uuid block-id] :logseq.property/choice-classes scoped-class-id]))))
758765

759766
(defn upsert-closed-value!
760767
"id should be a block UUID or nil"
761-
[conn property-id {:keys [id value description] :as opts}]
768+
[conn property-id {:keys [id value description _scoped-class-id] :as opts}]
762769
(assert (or (nil? id) (uuid? id)))
763770
(let [db @conn
764771
property (d/entity db property-id)
@@ -797,8 +804,8 @@
797804

798805
:else
799806
(let [tx-data (build-closed-value-tx @conn property resolved-value opts)]
807+
(prn :debug :tx-data tx-data)
800808
(ldb/transact! conn tx-data {:outliner-op :save-block})
801-
802809
(when (seq description)
803810
(if-let [desc-ent (and id (:logseq.property/description (d/entity db [:block/uuid id])))]
804811
(ldb/transact! conn

0 commit comments

Comments
 (0)