Skip to content

Commit

Permalink
ui: DestinationSearchSelect component (#3593)
Browse files Browse the repository at this point in the history
* add search select api

* add search select components

* add initial stories

* fix types

* add additional stories

* fix disable behavior

* handle errors

* fix hint and error
  • Loading branch information
mastercactapus authored Jan 15, 2024
1 parent e727346 commit 0161051
Show file tree
Hide file tree
Showing 8 changed files with 1,518 additions and 331 deletions.
1,274 changes: 949 additions & 325 deletions graphql2/generated.go

Large diffs are not rendered by default.

23 changes: 20 additions & 3 deletions graphql2/graph/destinations.graphqls
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
extend type Query {

# destinationTypes returns a list of destination types that can be used for
# notifications.
destinationTypes: [DestinationTypeInfo!]! @experimental(flagName: "dest-types")

# destinationFieldValidate validates a destination field value as valid or invalid.
#
# It does not guarantee that the value is valid for the destination type, only
# that it is valid for the field (i.e., syntax/formatting).
destinationFieldValidate(input: DestinationFieldValidateInput!): Boolean! @experimental(flagName: "dest-types")

destinationFieldSearch(input: DestinationFieldSearchInput!): FieldValueConnection!
destinationFieldValueName(input: DestinationFieldValidateInput!): String!

# destinationDisplayInfo returns the display information for a destination.
destinationDisplayInfo(input: DestinationInput!): DestinationDisplayInfo! @experimental(flagName: "dest-types")
}

# FieldValueConnection is a connection to a list of FieldValuePairs.
type FieldValueConnection {
nodes: [FieldValuePair!]!
pageInfo: PageInfo!
}

input DestinationFieldValidateInput {
destType: DestinationType! # the type of destination to validate
fieldID: ID! # the ID of the input field to validate
Expand All @@ -25,6 +30,15 @@ input DestinationFieldValidateInput {
# notifications.
scalar DestinationType

input DestinationFieldSearchInput {
destType: DestinationType! # the type of destination to search for
fieldID: ID! # the ID of the input field to search for
search: String # search string to match against
omit: [String!] # values/ids to omit from results
after: String # cursor to start search from
first: Int = 15 # number of results to return
}

# Destination represents a destination that can be used for notifications.
type Destination {
type: DestinationType!
Expand All @@ -45,6 +59,9 @@ type FieldValuePair {
fieldID: ID! # The ID of the input field that this value is for.

value: String! # The value of the input field.
label: String! @goField(forceResolver: true) # The user-friendly text for this value of the input field (e.g., if the value is a user ID, label would be the user's name).

isFavorite: Boolean! # if true, this value is a favorite for the user, only set for search results
}

input DestinationInput {
Expand Down
124 changes: 124 additions & 0 deletions graphql2/graphqlapp/destinationtypes.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,130 @@ const (
fieldScheduleID = "schedule-id"
)

type FieldValuePair App
type DestinationDisplayInfo App

func (a *App) FieldValuePair() graphql2.FieldValuePairResolver { return (*FieldValuePair)(a) }

func (a *FieldValuePair) Label(ctx context.Context, fvp *graphql2.FieldValuePair) (string, error) {
if fvp.Label != "" {
return fvp.Label, nil
}

app := (*App)(a)
switch fvp.FieldID {
case fieldSlackChanID:
ch, err := app.SlackStore.Channel(ctx, fvp.Value)
if err != nil {
return "", err
}
return ch.Name, nil
case fieldSlackUGID:
ug, err := app.SlackStore.UserGroup(ctx, fvp.Value)
if err != nil {
return "", err
}

return ug.Handle, nil
case fieldUserID:
u, err := app.FindOneUser(ctx, fvp.Value)
if err != nil {
return "", err
}
return u.Name, nil
case fieldRotationID:
r, err := app.FindOneRotation(ctx, fvp.Value)
if err != nil {
return "", err
}
return r.Name, nil
case fieldScheduleID:
s, err := app.FindOneSchedule(ctx, fvp.Value)
if err != nil {
return "", err
}
return s.Name, nil
}

return "", validation.NewGenericError("unsupported fieldID")
}

func (q *Query) DestinationFieldValueName(ctx context.Context, input graphql2.DestinationFieldValidateInput) (string, error) {
switch input.FieldID {
case fieldSlackChanID:
ch, err := q.SlackChannel(ctx, input.Value)
if err != nil {
return "", err
}

return ch.Name, nil
case fieldSlackUGID:
ug, err := q.SlackUserGroup(ctx, input.Value)
if err != nil {
return "", err
}

return ug.Handle, nil
}

return "", validation.NewGenericError("unsupported fieldID")
}

func (q *Query) DestinationFieldSearch(ctx context.Context, input graphql2.DestinationFieldSearchInput) (*graphql2.FieldValueConnection, error) {
switch input.FieldID {
case fieldSlackChanID:
res, err := q.SlackChannels(ctx, &graphql2.SlackChannelSearchOptions{
Omit: input.Omit,
First: input.First,
Search: input.Search,
After: input.After,
})
if err != nil {
return nil, err
}

var nodes []graphql2.FieldValuePair
for _, c := range res.Nodes {
nodes = append(nodes, graphql2.FieldValuePair{
FieldID: input.FieldID,
Value: c.ID,
Label: c.Name,
})
}

return &graphql2.FieldValueConnection{
Nodes: nodes,
PageInfo: res.PageInfo,
}, nil
case fieldSlackUGID:
res, err := q.SlackUserGroups(ctx, &graphql2.SlackUserGroupSearchOptions{
Omit: input.Omit,
First: input.First,
Search: input.Search,
After: input.After,
})
if err != nil {
return nil, err
}

var nodes []graphql2.FieldValuePair
for _, ug := range res.Nodes {
nodes = append(nodes, graphql2.FieldValuePair{
FieldID: input.FieldID,
Value: ug.ID,
Label: ug.Handle,
})
}

return &graphql2.FieldValueConnection{
Nodes: nodes,
PageInfo: res.PageInfo,
}, nil
}

return nil, validation.NewGenericError("unsupported fieldID")
}

func (q *Query) DestinationFieldValidate(ctx context.Context, input graphql2.DestinationFieldValidateInput) (bool, error) {
switch input.DestType {
case destTwilioSMS, destTwilioVoice:
Expand Down
20 changes: 18 additions & 2 deletions graphql2/models_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 0161051

Please sign in to comment.