Skip to content

Commit

Permalink
Add description to frontend; factor typeahead suggestions out of Type…
Browse files Browse the repository at this point in the history
…ahead service
  • Loading branch information
jonathonherbert committed Jul 10, 2024
1 parent 6f90c82 commit d0690b0
Show file tree
Hide file tree
Showing 17 changed files with 229 additions and 193 deletions.
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ val circeVersion = "0.14.4"
lazy val cql = project.in(file("."))
.enablePlugins(RiffRaffArtifact)
.settings(
scalaVersion := "3.3.1",
scalaVersion := "3.4.2",
libraryDependencies ++= Seq(
"org.apache.pekko" %% "pekko-actor-typed" % PekkoVersion,
"org.apache.pekko" %% "pekko-stream" % PekkoVersion,
Expand Down
2 changes: 1 addition & 1 deletion project/build.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
sbt.version=1.9.9
sbt.version=1.10.1
18 changes: 11 additions & 7 deletions prosemirror-client/src/cqlInput/CqlInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { CqlServiceInterface } from "../services/CqlService";
import { QueryChangeEventDetail } from "./dom";
import { createEditor } from "./editor";

const baseFontSize = "28px";
const baseBorderRadius = "5px";
const baseFontSize = 28;
const baseBorderRadius = 5;
const template = document.createElement("template");
template.innerHTML = `
<style>
Expand All @@ -21,7 +21,7 @@ template.innerHTML = `
display: inline-flex;
background-color: rgba(255,255,255,0.2);
margin: 0 5px;
border-radius: ${baseBorderRadius};
border-radius: ${baseBorderRadius}px;
}
chip {
Expand Down Expand Up @@ -53,18 +53,18 @@ template.innerHTML = `
#cql-input {
position: relative;
padding: 5px;
font-size: ${baseFontSize};
font-size: ${baseFontSize}px;
anchor-name: --cql-input;
border: 2px solid grey;
border-radius: ${baseBorderRadius};
border-radius: ${baseBorderRadius}px;
}
#cql-popover {
width: 250px;
width: 500px;
margin: 0;
padding: 0;
top: anchor(end);
font-size: ${baseFontSize};
font-size: ${baseFontSize}px;
position-anchor: --cql-input;
}
Expand All @@ -81,6 +81,10 @@ template.innerHTML = `
cursor: pointer;
}
.Cql__OptionDescription {
font-size: ${baseFontSize * 0.8}px;
}
.Cql__ChipWrapper--is-pending-delete chip {
background-color: darkred;
}
Expand Down
5 changes: 3 additions & 2 deletions prosemirror-client/src/cqlInput/TypeaheadPopover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { TextSelection } from "prosemirror-state";
type MenuItem = {
label: string;
value: string;
description: string;
};

export class TypeaheadPopover {
Expand Down Expand Up @@ -134,10 +135,10 @@ export class TypeaheadPopover {

private updateItems(items: MenuItem[]) {
this.popoverEl.innerHTML = items
.map(({ label }, index) => {
.map(({ label, description }, index) => {
return `<div class="Cql__Option ${
index === this.currentOptionIndex ? "Cql__Option--is-selected" : ""
}" data-index="${index}">${label}</div>`;
}" data-index="${index}"><div class="Cql__OptionLabel">${label}</div><div class="Cql__OptionDescription">${description}</div></div>`;
})
.join("");
}
Expand Down
Empty file.
2 changes: 1 addition & 1 deletion prosemirror-client/src/services/CqlService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ type Suggestions = {
};

type TextSuggestion = { suggestions: Array<TextSuggestionOption> };
type TextSuggestionOption = { label: string; value: string };
type TextSuggestionOption = { label: string; value: string, description: string };
type DateSuggestion = { validFrom?: string; validTo?: string };

export interface CqlServiceInterface {
Expand Down
12 changes: 9 additions & 3 deletions src/main/scala/Handler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@ import com.amazonaws.services.lambda.runtime.events.{
import io.circe.generic.auto._
import io.circe.syntax._
import util.Logging
import cql.lang.Cql

import scala.util.Try
import scala.concurrent.Await
import scala.concurrent.duration.DurationInt
import scala.jdk.CollectionConverters._
import scala.concurrent.Future

import com.gu.contentapi.client.GuardianContentClient
import cql.lang.{Cql, Typeahead, TypeaheadHelpersCapi}
class Handler
extends RequestHandler[
APIGatewayProxyRequestEvent,
Expand All @@ -24,7 +25,12 @@ class Handler
with QueryJson {
private implicit val ec: scala.concurrent.ExecutionContext =
scala.concurrent.ExecutionContext.global
private val cql = new Cql()

val guardianContentClient = new GuardianContentClient("test")
val typeaheadHelpers = new TypeaheadHelpersCapi(guardianContentClient)
val typeahead = new Typeahead(typeaheadHelpers.fieldResolvers, typeaheadHelpers.outputModifierResolvers)

private val cql = new Cql(typeahead)

def handleRequest(
event: APIGatewayProxyRequestEvent,
Expand Down
9 changes: 7 additions & 2 deletions src/main/scala/HttpServer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,15 @@ import io.circe.syntax.*
import com.github.pjfanning.pekkohttpcirce.*
import scala.io.StdIn
import scala.util.{Failure, Success, Try}
import cql.lang.Cql
import cql.lang.{Cql, Typeahead, TypeaheadHelpersCapi}
import com.gu.contentapi.client.GuardianContentClient

object HttpServer extends QueryJson {
val cql = new Cql()
val guardianContentClient = new GuardianContentClient("test")
val typeaheadHelpers = new TypeaheadHelpersCapi(guardianContentClient)
val typeahead = new Typeahead(typeaheadHelpers.fieldResolvers, typeaheadHelpers.outputModifierResolvers)

val cql = new Cql(typeahead)

def run(): Unit = {

Expand Down
7 changes: 1 addition & 6 deletions src/main/scala/lang/Cql.scala
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
package cql.lang

import scala.io.Source
import scala.util.{Failure, Success, Try}
import cql.lang.QueryList
import io.circe.generic.semiauto.*
import io.circe.Encoder

import scala.concurrent.Future
import com.gu.contentapi.client.GuardianContentClient
Expand All @@ -19,12 +16,10 @@ case class CqlResult(
error: Option[String] = None
)

class Cql:
class Cql(typeahead: Typeahead):
implicit val ec: scala.concurrent.ExecutionContext =
scala.concurrent.ExecutionContext.global
val guardianContentClient = new GuardianContentClient("test")
val typeaheadClient = new TypeaheadQueryClientTest()
val typeahead = new Typeahead(typeaheadClient)

def run(program: String): Future[CqlResult] =
val scanner = new Scanner(program)
Expand Down
114 changes: 17 additions & 97 deletions src/main/scala/lang/Typeahead.scala
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,8 @@ type TypeaheadResolver = (String => Future[List[TextSuggestionOption]]) |

type TypeaheadType = "TEXT" | "DATE"

class Typeahead(client: TypeaheadQueryClient) {
private val typeaheadTokenResolverMap = Map(
TokenType.QUERY_FIELD_KEY -> suggestFieldKey,
TokenType.QUERY_OUTPUT_MODIFIER_KEY -> suggestOutputModifierKey,
TokenType.QUERY_VALUE -> suggestFieldValue
)

case class TypeaheadField(
id: String,
name: String,
description: String,
resolver: TypeaheadResolver,
Expand All @@ -56,88 +50,20 @@ class Typeahead(client: TypeaheadQueryClient) {
def toSuggestionOption: TextSuggestionOption =
TextSuggestionOption(name, name, description)

private val typeaheadFieldResolverMap = Map(
"tag" -> TypeaheadField(
"Tag",
"Search by content tags, e.g. sport/football",
suggestTags
),
"section" -> TypeaheadField(
"Section",
"Search by content sections, e.g. section/news",
suggestSections
)
class Typeahead(fieldResolvers: List[TypeaheadField], outputModifierResolvers: List[TypeaheadField]) {
private val typeaheadTokenResolverMap = Map(
TokenType.QUERY_FIELD_KEY -> suggestFieldKey,
TokenType.QUERY_OUTPUT_MODIFIER_KEY -> suggestOutputModifierKey,
TokenType.QUERY_VALUE -> suggestFieldValue
)

private val typeaheadFieldResolverEntries = TextSuggestion(
typeaheadFieldResolverMap.map {
case (value, TypeaheadField(label, description, _, _)) =>
TextSuggestionOption(label, value, description)
private val typeaheadFieldEntries = TextSuggestion(
fieldResolvers.map {
case TypeaheadField(id, label, description, _, _) =>
TextSuggestionOption(label, id, description)
}.toList
)

private val typeaheadOutputModifierResolvers = List(
TypeaheadField(
"show-fields",
"Determine the list of fields to return",
List(
TextSuggestionOption("all", "all", "Description"),
TextSuggestionOption("trailText", "trailText", "Description"),
TextSuggestionOption("headline", "headline", "Description"),
TextSuggestionOption(
"showInRelatedContent",
"showInRelatedContent",
"Description"
),
TextSuggestionOption("body", "body", "Description"),
TextSuggestionOption("lastModified", "lastModified", "Description"),
TextSuggestionOption(
"hasStoryPackage",
"hasStoryPackage",
"Description"
),
TextSuggestionOption("score", "score", "Description"),
TextSuggestionOption("standfirst", "standfirst", "Description"),
TextSuggestionOption("shortUrl", "shortUrl", "Description"),
TextSuggestionOption("thumbnail", "thumbnail", "Description"),
TextSuggestionOption("wordcount", "wordcount", "Description"),
TextSuggestionOption("commentable", "commentable", "Description"),
TextSuggestionOption("isPremoderated", "isPremoderated", "Description"),
TextSuggestionOption("allowUgc", "allowUgc", "Description"),
TextSuggestionOption("byline", "byline", "Description"),
TextSuggestionOption("publication", "publication", "Description"),
TextSuggestionOption(
"internalPageCode",
"internalPageCode",
"Description"
),
TextSuggestionOption(
"productionOffice",
"productionOffice",
"Description"
),
TextSuggestionOption(
"shouldHideAdverts",
"shouldHideAdverts",
"Description"
),
TextSuggestionOption(
"liveBloggingNow",
"liveBloggingNow",
"Description"
),
TextSuggestionOption(
"commentCloseDate",
"commentCloseDate",
"Description"
),
TextSuggestionOption("starRating", "starRating", "Description")
)
),
TypeaheadField("from-date", "The date to search from", List.empty),
TypeaheadField("to-date", "The date to search to", List.empty)
)

def getSuggestions(
program: QueryList
): Future[List[TypeaheadSuggestion]] =
Expand Down Expand Up @@ -235,26 +161,26 @@ class Typeahead(client: TypeaheadQueryClient) {

private def suggestFieldKey(str: String): TextSuggestion =
str match
case "" => typeaheadFieldResolverEntries
case "" => typeaheadFieldEntries
case str =>
typeaheadFieldResolverEntries.copy(
suggestions = typeaheadFieldResolverEntries.suggestions
typeaheadFieldEntries.copy(
suggestions = typeaheadFieldEntries.suggestions
.filter(_.value.contains(str.toLowerCase()))
)

private def suggestFieldValue(
key: String,
str: String
): Future[List[TextSuggestionOption]] =
typeaheadFieldResolverMap
.get(key)
fieldResolvers
.find(_.id == key)
.map(_.resolveSuggestions(str))
.getOrElse(Future.successful(List.empty))

private def suggestOutputModifierKey(str: String): Future[TextSuggestion] =
Future.successful(
TextSuggestion(
typeaheadOutputModifierResolvers
outputModifierResolvers
.filter(
_.name.contains(str.toLowerCase())
)
Expand All @@ -266,7 +192,7 @@ class Typeahead(client: TypeaheadQueryClient) {
key: String,
str: String
): Future[TextSuggestion | DateSuggestion] =
typeaheadOutputModifierResolvers.find(_.name == key) match
outputModifierResolvers.find(_.name == key) match
case Some(typeaheadField) if typeaheadField.suggestionType == "TEXT" =>
typeaheadField.resolveSuggestions(str).map { suggestions =>
TextSuggestion(
Expand All @@ -282,10 +208,4 @@ class Typeahead(client: TypeaheadQueryClient) {
case Some(typeaheadField) if typeaheadField.suggestionType == "DATE" =>
Future.successful(DateSuggestion(None, None))
case _ => Future.successful(TextSuggestion(List.empty))

private def suggestTags(str: String) =
client.getTags(str)

private def suggestSections(str: String) =
client.getSections(str)
}
Loading

0 comments on commit d0690b0

Please sign in to comment.