Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[UI idea] Tags dropdown #4759

Merged
merged 9 commits into from
Nov 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 15 additions & 6 deletions templates/customize-adventure.html
Original file line number Diff line number Diff line change
Expand Up @@ -34,22 +34,31 @@ <h3 class="text-center mt-0 mb-4">{{_('general_settings')}}</h3>

<div class="flex flex-row items-center mb-4">
<label for="tags" class="inline-block w-40 text-xl">{{_('tags')}}</label>
<div class="flex flex-row w-full">
<input type="text" name="tag" placeholder="{{_('tag_input_placeholder')}}" id="input_adventure_tags"
_="on keypress if event.keyCode == 13 then event.stopPropagation()"
<div class="flex flex-row w-full relative" id="add_tags">
<input type="text" name="tag" placeholder="{{_('tag_input_placeholder')}}" id="search_tags_input"
class="appearance-none w-full bg-gray-200 border border-gray-200 text-gray-700 py-3 px-4 ltr:pr-8 rtl:pl-8 rounded"
hx-trigger="enter"
hx-post="/tags/create/{{adventure.id}}" hx-target="#tags-list" hx-swap="outerHTML"
_="on htmx:afterRequest focus()"
class="appearance-none w-full bg-gray-200 border border-gray-200 text-gray-700 py-3 px-4 ltr:pr-8 rtl:pl-8 rounded"
_="on click event.stopPropagation()
on keypress if event.keyCode == 13 event.stopPropagation() end
on htmx:afterRequest focus()"
>
<button class="green-btn" id="add_adventure_tags"
hx-trigger="click"
hx-post="/tags/create/{{adventure.id}}"
hx-target="#tags-list"
hx-swap="outerHTML"
hx-include="[name='tag']"
_="on htmx:afterRequest set value of #input_adventure_tags to ''"
_="on htmx:afterRequest set value of #search_tags_input to ''"
>{{_('add')}}</button>

<div
id="tags-dropdown"
hx-trigger="load"
hx-get="/tags?adventure_id={{adventure.id}}"
hx-target="#tags-dropdown"
hx-swap="outerHTML"
></div>
</div>
</div>
{{ render_partial('htmx-tags-list.html', tags=adventure_tags, adventure_id=adventure.id, creator=adventure.creator) }}
Expand Down
17 changes: 17 additions & 0 deletions templates/htmx-tags-dropdown-item.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!-- Requires #search_tags_input to exist -->
<div class="relative cursor-pointer !w-full border-2 rounded-lg border-green-300 p-2 px-5 m-1 shadow"
id="tag_{{tag['name']}}" hx-post="/tags/create/{{adventure_id}}/{{tag.name}}" hx-target="#tags-list"
hx-swap="outerHTML" _="on click event.stopPropagation()
on htmx:afterRequest
if detail.xhr.status == 200
remove me
set #tags_dropdown's *display to 'none'
call #search_tags_input's focus()
end
on keyup from #search_tags_input
set v to its value
if not my innerHTML.includes(v.trim())
set my *display to 'none'
else
set my *display to 'block'
">{{tag["name"]}}</div>
12 changes: 12 additions & 0 deletions templates/htmx-tags-dropdown.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!-- Requires #search_tags_input to exist -->
<div id="tags_dropdown" class="dropdown-menu dropdown-blue z-20 min-w-full flex-wrap !border-gray-400"
style="display: none; width: 100%; opacity: 0; border: none; top: 100%; padding: 0px !important; margin: 0px; margin-top: 0.25rem;"
_="on click event.stopPropagation() end
on keyup or focus from #search_tags_input debounced at 500ms
set my *display to 'flex'
transition my *opacity to 1 over 500ms end
">
{% for tag in tags %}
{{ render_partial('htmx-tags-dropdown-item.html', tag=tag, adventure_id=adventure_id) }}
{% endfor %}
</div>
22 changes: 5 additions & 17 deletions templates/htmx-tags-list.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,23 @@
<ul id="tags-list" class="flex flex-wrap items-center mb-4 list-none">
{% for tag in tags %}
<!-- TODO: allow teacher to edit tags; not urgent, but for a better ux! -->
<!-- _="on click toggle .hidden on #update-tag-input_{{loop.index}}
set the value of #update-tag-input_{{loop.index}} to '{{tag}}'

toggle .hidden on me" -->
<li
id="tag_{{loop.index}}"
class='relative border-2 rounded-lg border-green-300 p-2 px-5 mx-2 mb-2 shadow'
>
class='relative border-2 rounded-lg border-green-300 p-2 px-5 mx-2 mb-2 shadow'>
{{tag}}
{% if not public and username == creator %}
<span class='absolute top-0.5 right-0.5 text-gray-600 hover:text-red-400 fa-regular fa-circle-xmark'
{% if adventure_id %}
hx-target="#tags_dropdown"
hx-delete="/tags/delete/{{tag}}/{{adventure_id}}"
hx-swap="none"
_="on htmx:afterRequest if detail.xhr.status == 200 then remove #tag_{{loop.index}}
if JSON.parse(detail.xhr.response).tags.length == 0 toggle .hidden on #no-tags"
hx-swap="beforeend"
_="on htmx:afterRequest
if detail.xhr.status == 200 then remove #tag_{{loop.index}} end"
{% else %}
_='on click remove #tag_{{loop.index}}'
{% endif %}
></span>
{% endif %}
</li>
<!-- <input type="text"
autofocus
id="update-tag-input_{{loop.index}}"
class='relative border-2 rounded-lg border-green-400 p-2 px-5 mx-2 mb-2 hidden'
_="on blur toggle .hidden on me then toggle .hidden on #tag_{{loop.index}}"
value="{{tag}}"
> -->
{% endfor %}
<li id="no-tags" class='border-2 rounded-lg border-green-400 p-2 px-5 mx-2 mb-2 {% if tags|length > 0 %}hidden{% endif %}'>{{_('no_tags')}}</li>
</ul>
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ describe("Tags of adventures", () => {
})

it("has tags input and button", () => {
cy.get("#input_adventure_tags")
cy.get("#search_tags_input")
.should("be.visible")
.should("be.empty")

Expand All @@ -18,26 +18,23 @@ describe("Tags of adventures", () => {

it("has no tags initially", () => {
cy.get("#tags-list")
.should("be.visible")
.get("#no-tags")
.should("be.visible")
.should("be.not.visible")
})

it("adds a tag to adventure by pressing enter within the input field", () => {
cy.get("#input_adventure_tags")
cy.get("#search_tags_input")
.should("be.empty")
.type("statements{enter}")
cy.wait(500)
cy.get("#tags-list")
.should("be.visible")
.get("#no-tags")
.should("be.hidden")

cy.get("#tags-list li")
.should("include.text", "statements")
})

it("adds a tag to adventure by pressing the add button", () => {
cy.get("#input_adventure_tags")
cy.get("#search_tags_input")
.should("be.empty")
.type("training")
cy.get("#add_adventure_tags")
Expand All @@ -46,8 +43,7 @@ describe("Tags of adventures", () => {
cy.wait(500)
cy.get("#tags-list")
.should("be.visible")
.get("#no-tags")
.should("be.hidden")

cy.get("#tags-list li")
.should("include.text", "training")
})
Expand All @@ -59,7 +55,7 @@ describe("Tags of adventures", () => {
times: 1,
}).as("createTag")

cy.get("#input_adventure_tags")
cy.get("#search_tags_input")
.should("be.empty")
.type("training{enter}")
cy.wait("@createTag").should('have.nested.property', 'response.statusCode', 400)
Expand Down
12 changes: 12 additions & 0 deletions website/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,15 @@ def read_tag(self, tag_name):
def read_tags(self, tags):
return [self.read_tag(name) for name in tags]

def read_public_tags(self):
"""Public tags are tagged within one or more public adventure or those that aren't in use."""
all_tags = TAGS.scan()
public_tags = []
for tag in all_tags:
if not tag["tagged_in"] or any([adv["public"] for adv in tag["tagged_in"]]):
public_tags.append(tag)
return public_tags

def read_tags_by_username(self, username):
tags = TAGS.get_many({"creator": username})
return tags if tags else {}
Expand Down Expand Up @@ -645,6 +654,9 @@ def get_second_teacher_adventures(self, classes, teacher):
def all_adventures(self):
return ADVENTURES.scan()

def public_adventures(self):
return ADVENTURES.get_many({"public": True})

def get_student_classes_ids(self, username):
ids = USERS.get({"username": username}).get("classes")
return list(ids) if ids else []
Expand Down
18 changes: 15 additions & 3 deletions website/tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,20 @@ def __init__(self, db: Database, achievements: Achievements):
self.db = db
self.achievements = achievements

@route("/<adventure_id>", methods=["GET"])
@route("/", methods=["GET"], defaults={"adventure_id": None})
@requires_teacher
def get_public_tags(self, user, adventure_id):
public_tags = self.db.read_public_tags()
adventure_id = request.args.get("adventure_id")
if adventure_id:
adventure = self.db.get_adventure(adventure_id)
# exclude current adventure's tags
public_tags = list(filter(lambda t: t["name"] not in adventure.get("tags", []), public_tags))

return jinja_partials.render_partial('htmx-tags-dropdown.html',
tags=public_tags, adventure_id=adventure_id, creator=user)

@route("/create/<adventure_id>", methods=["POST"])
@route("/create/<adventure_id>/<new_tag>", methods=["POST"])
@requires_teacher
Expand Down Expand Up @@ -91,6 +105,4 @@ def delete_from_adventure(self, user, tag, adventure_id):
adventure_tags = list(filter(lambda name: name != tag_name, adventure_tags))
self.db.update_adventure(adventure_id, {"tags": adventure_tags})

return adventure_tags, 200
# return jinja_partials.render_partial('htmx-tags-list.html', tags=adventure_tags,
# adventure_id=adventure_id, creator=user["username"])
return jinja_partials.render_partial('htmx-tags-dropdown-item.html', tag=db_tag, adventure_id=adventure_id)
Loading