Fix tags autocomplete
Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
parent
6bb0b6d08a
commit
e9e12500dc
|
@ -29,19 +29,28 @@
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Component, Prop, Vue } from "vue-property-decorator";
|
import { Component, Prop, Vue } from "vue-property-decorator";
|
||||||
import get from "lodash/get";
|
|
||||||
import differenceBy from "lodash/differenceBy";
|
import differenceBy from "lodash/differenceBy";
|
||||||
import { ITag } from "../../types/tag.model";
|
import { ITag } from "../../types/tag.model";
|
||||||
|
import { FILTER_TAGS } from "@/graphql/tags";
|
||||||
|
|
||||||
@Component
|
@Component({
|
||||||
|
apollo: {
|
||||||
|
tags: {
|
||||||
|
query: FILTER_TAGS,
|
||||||
|
variables() {
|
||||||
|
return {
|
||||||
|
filter: this.text,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
export default class TagInput extends Vue {
|
export default class TagInput extends Vue {
|
||||||
@Prop({ required: false, default: () => [] }) data!: ITag[];
|
|
||||||
|
|
||||||
@Prop({ required: true, default: "value" }) path!: string;
|
|
||||||
|
|
||||||
@Prop({ required: true }) value!: ITag[];
|
@Prop({ required: true }) value!: ITag[];
|
||||||
|
|
||||||
filteredTags: ITag[] = [];
|
tags!: ITag[];
|
||||||
|
|
||||||
|
text = "";
|
||||||
|
|
||||||
private static componentId = 0;
|
private static componentId = 0;
|
||||||
|
|
||||||
|
@ -53,13 +62,20 @@ export default class TagInput extends Vue {
|
||||||
return `tag-input-${TagInput.componentId}`;
|
return `tag-input-${TagInput.componentId}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
getFilteredTags(text: string): void {
|
async getFilteredTags(text: string): Promise<void> {
|
||||||
this.filteredTags = differenceBy(this.data, this.value, "id").filter(
|
this.text = text;
|
||||||
|
await this.$apollo.queries.tags.refetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
get filteredTags(): ITag[] {
|
||||||
|
return differenceBy(this.tags, this.value, "id").filter(
|
||||||
(option) =>
|
(option) =>
|
||||||
get(option, this.path)
|
option.title
|
||||||
.toString()
|
.toString()
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.indexOf(text.toLowerCase()) >= 0
|
.indexOf(this.text.toLowerCase()) >= 0 ||
|
||||||
|
option.slug.toString().toLowerCase().indexOf(this.text.toLowerCase()) >=
|
||||||
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,16 +9,22 @@ export const TAG_FRAGMENT = gql`
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const TAGS = gql`
|
export const TAGS = gql`
|
||||||
query {
|
query Tags {
|
||||||
tags {
|
tags {
|
||||||
id
|
|
||||||
related {
|
related {
|
||||||
id
|
...TagFragment
|
||||||
slug
|
|
||||||
title
|
|
||||||
}
|
}
|
||||||
slug
|
...TagFragment
|
||||||
title
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
${TAG_FRAGMENT}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const FILTER_TAGS = gql`
|
||||||
|
query FilterTags($filter: String) {
|
||||||
|
tags(filter: $filter) {
|
||||||
|
...TagFragment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
${TAG_FRAGMENT}
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
/>
|
/>
|
||||||
</b-field>
|
</b-field>
|
||||||
|
|
||||||
<tag-input v-model="event.tags" :data="tags" path="title" />
|
<tag-input v-model="event.tags" />
|
||||||
|
|
||||||
<b-field
|
<b-field
|
||||||
horizontal
|
horizontal
|
||||||
|
@ -556,8 +556,6 @@ import {
|
||||||
IPerson,
|
IPerson,
|
||||||
usernameWithDomain,
|
usernameWithDomain,
|
||||||
} from "../../types/actor";
|
} from "../../types/actor";
|
||||||
import { TAGS } from "../../graphql/tags";
|
|
||||||
import { ITag } from "../../types/tag.model";
|
|
||||||
import {
|
import {
|
||||||
buildFileFromIMedia,
|
buildFileFromIMedia,
|
||||||
buildFileVariable,
|
buildFileVariable,
|
||||||
|
@ -590,7 +588,6 @@ const DEFAULT_LIMIT_NUMBER_OF_PLACES = 10;
|
||||||
},
|
},
|
||||||
apollo: {
|
apollo: {
|
||||||
currentActor: CURRENT_ACTOR_CLIENT,
|
currentActor: CURRENT_ACTOR_CLIENT,
|
||||||
tags: TAGS,
|
|
||||||
config: CONFIG,
|
config: CONFIG,
|
||||||
identities: IDENTITIES,
|
identities: IDENTITIES,
|
||||||
event: {
|
event: {
|
||||||
|
@ -643,8 +640,6 @@ export default class EditEvent extends Vue {
|
||||||
|
|
||||||
currentActor!: IActor;
|
currentActor!: IActor;
|
||||||
|
|
||||||
tags: ITag[] = [];
|
|
||||||
|
|
||||||
event: IEvent = new EventModel();
|
event: IEvent = new EventModel();
|
||||||
|
|
||||||
unmodifiedEvent: IEvent = new EventModel();
|
unmodifiedEvent: IEvent = new EventModel();
|
||||||
|
|
|
@ -67,7 +67,7 @@
|
||||||
/>
|
/>
|
||||||
</b-field>
|
</b-field>
|
||||||
|
|
||||||
<tag-input v-model="editablePost.tags" :data="tags" path="title" />
|
<tag-input v-model="editablePost.tags" />
|
||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label">{{ $t("Post") }}</label>
|
<label class="label">{{ $t("Post") }}</label>
|
||||||
|
@ -166,7 +166,6 @@ import {
|
||||||
} from "@/utils/image";
|
} from "@/utils/image";
|
||||||
import GroupMixin from "@/mixins/group";
|
import GroupMixin from "@/mixins/group";
|
||||||
import { PostVisibility } from "@/types/enums";
|
import { PostVisibility } from "@/types/enums";
|
||||||
import { TAGS } from "../../graphql/tags";
|
|
||||||
import { CONFIG } from "../../graphql/config";
|
import { CONFIG } from "../../graphql/config";
|
||||||
import {
|
import {
|
||||||
FETCH_POST,
|
FETCH_POST,
|
||||||
|
@ -187,7 +186,6 @@ import { FETCH_GROUP } from "@/graphql/group";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
apollo: {
|
apollo: {
|
||||||
tags: TAGS,
|
|
||||||
config: CONFIG,
|
config: CONFIG,
|
||||||
group: {
|
group: {
|
||||||
query: FETCH_GROUP,
|
query: FETCH_GROUP,
|
||||||
|
|
|
@ -7,8 +7,9 @@ defmodule Mobilizon.GraphQL.Resolvers.Tag do
|
||||||
alias Mobilizon.Events.{Event, Tag}
|
alias Mobilizon.Events.{Event, Tag}
|
||||||
alias Mobilizon.Posts.Post
|
alias Mobilizon.Posts.Post
|
||||||
|
|
||||||
def list_tags(_parent, %{page: page, limit: limit}, _resolution) do
|
def list_tags(_parent, %{page: page, limit: limit} = args, _resolution) do
|
||||||
tags = Mobilizon.Events.list_tags(page, limit)
|
filter = Map.get(args, :filter)
|
||||||
|
tags = Mobilizon.Events.list_tags(filter, page, limit)
|
||||||
|
|
||||||
{:ok, tags}
|
{:ok, tags}
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,33 +6,33 @@ defmodule Mobilizon.GraphQL.Resolvers.TagTest do
|
||||||
alias Mobilizon.GraphQL.AbsintheHelpers
|
alias Mobilizon.GraphQL.AbsintheHelpers
|
||||||
|
|
||||||
describe "Tag Resolver" do
|
describe "Tag Resolver" do
|
||||||
test "list_tags/3 returns the list of tags", context do
|
@tags_query """
|
||||||
|
query Tags($filter: String) {
|
||||||
|
tags(filter: $filter) {
|
||||||
|
id
|
||||||
|
related {
|
||||||
|
id
|
||||||
|
slug
|
||||||
|
title
|
||||||
|
}
|
||||||
|
slug
|
||||||
|
title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
test "list_tags/3 returns the list of tags", %{conn: conn} do
|
||||||
tag1 = insert(:tag)
|
tag1 = insert(:tag)
|
||||||
tag2 = insert(:tag)
|
tag2 = insert(:tag)
|
||||||
tag3 = insert(:tag)
|
tag3 = insert(:tag)
|
||||||
insert(:tag_relation, tag: tag1, link: tag2)
|
insert(:tag_relation, tag: tag1, link: tag2)
|
||||||
insert(:tag_relation, tag: tag3, link: tag1)
|
insert(:tag_relation, tag: tag3, link: tag1)
|
||||||
|
|
||||||
query = """
|
|
||||||
{
|
|
||||||
tags {
|
|
||||||
id,
|
|
||||||
slug,
|
|
||||||
title,
|
|
||||||
related {
|
|
||||||
id,
|
|
||||||
title,
|
|
||||||
slug
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
|
|
||||||
res =
|
res =
|
||||||
context.conn
|
conn
|
||||||
|> get("/api", AbsintheHelpers.query_skeleton(query, "tags"))
|
|> AbsintheHelpers.graphql_query(query: @tags_query)
|
||||||
|
|
||||||
tags = json_response(res, 200)["data"]["tags"]
|
tags = res["data"]["tags"]
|
||||||
assert tags |> length == 3
|
assert tags |> length == 3
|
||||||
|
|
||||||
assert tags
|
assert tags
|
||||||
|
@ -45,5 +45,19 @@ defmodule Mobilizon.GraphQL.Resolvers.TagTest do
|
||||||
|> Enum.map(fn tag -> tag.slug end)
|
|> Enum.map(fn tag -> tag.slug end)
|
||||||
|> MapSet.new()
|
|> MapSet.new()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "list_tags/3 returns tags for a filter", %{conn: conn} do
|
||||||
|
tag1 = insert(:tag, title: "PineApple", slug: "pineapple")
|
||||||
|
tag2 = insert(:tag, title: "sexy pineapple", slug: "sexy-pineapple")
|
||||||
|
_tag3 = insert(:tag)
|
||||||
|
|
||||||
|
res =
|
||||||
|
conn
|
||||||
|
|> AbsintheHelpers.graphql_query(query: @tags_query, variables: %{filter: "apple"})
|
||||||
|
|
||||||
|
tags = res["data"]["tags"]
|
||||||
|
assert tags |> length == 2
|
||||||
|
assert [tag1.id, tag2.id] == tags |> Enum.map(&String.to_integer(&1["id"]))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -242,6 +242,12 @@ defmodule Mobilizon.EventsTest do
|
||||||
assert [tag.id] == Events.list_tags() |> Enum.map(& &1.id)
|
assert [tag.id] == Events.list_tags() |> Enum.map(& &1.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "list_tags/1 filters tags by title or slug" do
|
||||||
|
tag1 = insert(:tag, title: "PineApple", slug: "pineapple")
|
||||||
|
tag2 = insert(:tag, title: "sexy pineapple", slug: "sexy-pineapple")
|
||||||
|
assert [tag1.id, tag2.id] == Events.list_tags("apple") |> Enum.map(& &1.id)
|
||||||
|
end
|
||||||
|
|
||||||
test "get_tag!/1 returns the tag with given id" do
|
test "get_tag!/1 returns the tag with given id" do
|
||||||
tag = insert(:tag)
|
tag = insert(:tag)
|
||||||
assert Events.get_tag!(tag.id).id == tag.id
|
assert Events.get_tag!(tag.id).id == tag.id
|
||||||
|
|
Loading…
Reference in a new issue