From bc5c24defbf114e303ab3e22e95666e89ab0d4ff Mon Sep 17 00:00:00 2001 From: Mathias Vagni Date: Thu, 14 Sep 2023 15:21:00 +0100 Subject: [PATCH 1/2] Add ui component builder --- .changeset/plenty-crabs-sell.md | 5 ++ src/examples/createThread.ts | 61 ++++++++++++++++++++++++ src/index.ts | 2 + src/ui-components/badgeComponent.ts | 29 +++++++++++ src/ui-components/containerComponent.ts | 12 +++++ src/ui-components/copyButtonComponent.ts | 13 +++++ src/ui-components/dividerComponent.ts | 28 +++++++++++ src/ui-components/index.test.ts | 60 +++++++++++++++++++++++ src/ui-components/index.ts | 21 ++++++++ src/ui-components/linkButtonComponent.ts | 13 +++++ src/ui-components/plainTextComponent.ts | 48 +++++++++++++++++++ src/ui-components/rowComponent.ts | 16 +++++++ src/ui-components/spacerComponent.ts | 28 +++++++++++ src/ui-components/textComponent.ts | 40 ++++++++++++++++ 14 files changed, 376 insertions(+) create mode 100644 .changeset/plenty-crabs-sell.md create mode 100644 src/examples/createThread.ts create mode 100644 src/ui-components/badgeComponent.ts create mode 100644 src/ui-components/containerComponent.ts create mode 100644 src/ui-components/copyButtonComponent.ts create mode 100644 src/ui-components/dividerComponent.ts create mode 100644 src/ui-components/index.test.ts create mode 100644 src/ui-components/index.ts create mode 100644 src/ui-components/linkButtonComponent.ts create mode 100644 src/ui-components/plainTextComponent.ts create mode 100644 src/ui-components/rowComponent.ts create mode 100644 src/ui-components/spacerComponent.ts create mode 100644 src/ui-components/textComponent.ts diff --git a/.changeset/plenty-crabs-sell.md b/.changeset/plenty-crabs-sell.md new file mode 100644 index 0000000..df98220 --- /dev/null +++ b/.changeset/plenty-crabs-sell.md @@ -0,0 +1,5 @@ +--- +'@team-plain/typescript-sdk': minor +--- + +Add `uiComponent` builder so you can more easily create ui components for use with the API. diff --git a/src/examples/createThread.ts b/src/examples/createThread.ts new file mode 100644 index 0000000..a9616c1 --- /dev/null +++ b/src/examples/createThread.ts @@ -0,0 +1,61 @@ +import { PlainClient } from '../client'; +import { ComponentTextColor, ComponentTextSize } from '../graphql/types'; +import { uiComponent } from '../ui-components'; + +export async function createThreadA() { + const client = new PlainClient({ apiKey: 'XXX' }); + + const res = await client.createThread({ + title: 'Contact form', + customerIdentifier: { + customerId: 'c_123', + }, + components: [ + { + componentText: { + text: 'hello world', + }, + }, + { + componentText: { + text: 'hello world', + textColor: ComponentTextColor.Error, + }, + }, + { + componentText: { + text: 'hello world', + textSize: ComponentTextSize.M, + }, + }, + ], + }); + + if (res.error) { + console.error(res.error); + } else { + console.log(`Thread created with id=${res.data.thread.id}`); + } +} + +export async function createThreadB() { + const client = new PlainClient({ apiKey: 'XXX' }); + + const res = await client.createThread({ + title: 'Contact form', + customerIdentifier: { + customerId: 'c_123', + }, + components: [ + uiComponent.text({ text: 'hello world' }), + uiComponent.text({ text: 'hello world', color: 'ERROR' }), + uiComponent.text({ text: 'hello world', size: 'M' }), + ], + }); + + if (res.error) { + console.error(res.error); + } else { + console.log(`Thread created with id=${res.data.thread.id}`); + } +} diff --git a/src/index.ts b/src/index.ts index ee3b3ee..b94de59 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,6 +2,8 @@ export { PlainClient } from './client'; +export { uiComponent } from './ui-components'; + export { // Enums AttachmentType, diff --git a/src/ui-components/badgeComponent.ts b/src/ui-components/badgeComponent.ts new file mode 100644 index 0000000..9c3d5af --- /dev/null +++ b/src/ui-components/badgeComponent.ts @@ -0,0 +1,29 @@ +import { ComponentBadgeColor, type ComponentInput } from '../graphql/types'; + +type Color = `${ComponentBadgeColor}`; + +function colorToEnum(color?: Color): ComponentBadgeColor | null { + if (!color) { + return null; + } + const map: Record = { + BLUE: ComponentBadgeColor.Blue, + GREEN: ComponentBadgeColor.Green, + GREY: ComponentBadgeColor.Grey, + RED: ComponentBadgeColor.Red, + YELLOW: ComponentBadgeColor.Yellow, + }; + return map[color]; +} + +/** + * Returns a ComponentInput which can be used with the API. + */ +export function badgeComponent(args: { label: string; color?: Color }): ComponentInput { + return { + componentBadge: { + badgeLabel: args.label, + badgeColor: colorToEnum(args.color), + }, + }; +} diff --git a/src/ui-components/containerComponent.ts b/src/ui-components/containerComponent.ts new file mode 100644 index 0000000..c4c6aa1 --- /dev/null +++ b/src/ui-components/containerComponent.ts @@ -0,0 +1,12 @@ +import { type ComponentInput } from '../graphql/types'; + +/** + * Returns a ComponentInput which can be used with the API. + */ +export function containerComponent(args: { content: ComponentInput[] }): ComponentInput { + return { + componentContainer: { + containerContent: args.content, + }, + }; +} diff --git a/src/ui-components/copyButtonComponent.ts b/src/ui-components/copyButtonComponent.ts new file mode 100644 index 0000000..7cadc4c --- /dev/null +++ b/src/ui-components/copyButtonComponent.ts @@ -0,0 +1,13 @@ +import { type ComponentInput } from '../graphql/types'; + +/** + * Returns a ComponentInput which can be used with the API. + */ +export function copyButtonComponent(args: { value: string; tooltip?: string }): ComponentInput { + return { + componentCopyButton: { + copyButtonValue: args.value, + copyButtonTooltipLabel: args.tooltip, + }, + }; +} diff --git a/src/ui-components/dividerComponent.ts b/src/ui-components/dividerComponent.ts new file mode 100644 index 0000000..0957488 --- /dev/null +++ b/src/ui-components/dividerComponent.ts @@ -0,0 +1,28 @@ +import { ComponentDividerSpacingSize, type ComponentInput } from '../graphql/types'; + +type Size = `${ComponentDividerSpacingSize}`; + +function spacingSizeToEnum(size?: Size): ComponentDividerSpacingSize | null { + if (!size) { + return null; + } + const map: Record = { + XL: ComponentDividerSpacingSize.Xl, + L: ComponentDividerSpacingSize.L, + M: ComponentDividerSpacingSize.M, + S: ComponentDividerSpacingSize.S, + XS: ComponentDividerSpacingSize.Xs, + }; + return map[size]; +} + +/** + * Returns a ComponentInput which can be used with the API. + */ +export function dividerComponent(args: { spacingSize?: Size }): ComponentInput { + return { + componentDivider: { + dividerSpacingSize: spacingSizeToEnum(args.spacingSize), + }, + }; +} diff --git a/src/ui-components/index.test.ts b/src/ui-components/index.test.ts new file mode 100644 index 0000000..9136d2d --- /dev/null +++ b/src/ui-components/index.test.ts @@ -0,0 +1,60 @@ +import { describe, expect, test } from 'vitest'; + +import { ComponentTextColor, ComponentTextSize } from '../../dist'; +import { ComponentBadgeColor } from '../graphql/types'; +import { uiComponent } from '.'; + +describe('ui components builder', () => { + test('basic example', () => { + expect([ + uiComponent.text({ text: 'hello world' }), + uiComponent.text({ text: 'hello world', color: 'MUTED', size: 'S' }), + ]).toEqual([ + { + componentText: { + text: 'hello world', + textColor: null, + textSize: null, + }, + }, + { + componentText: { + text: 'hello world', + textColor: ComponentTextColor.Muted, + textSize: ComponentTextSize.S, + }, + }, + ]); + }); + + test('container component', () => { + expect([ + uiComponent.container({ + content: [ + uiComponent.text({ text: 'hello world' }), + uiComponent.badge({ label: 'success', color: 'GREEN' }), + ], + }), + ]).toEqual([ + { + componentContainer: { + containerContent: [ + { + componentText: { + text: 'hello world', + textColor: null, + textSize: null, + }, + }, + { + componentBadge: { + badgeLabel: 'success', + badgeColor: ComponentBadgeColor.Green, + }, + }, + ], + }, + }, + ]); + }); +}); diff --git a/src/ui-components/index.ts b/src/ui-components/index.ts new file mode 100644 index 0000000..fb6790a --- /dev/null +++ b/src/ui-components/index.ts @@ -0,0 +1,21 @@ +import { badgeComponent } from './badgeComponent'; +import { containerComponent } from './containerComponent'; +import { copyButtonComponent } from './copyButtonComponent'; +import { dividerComponent } from './dividerComponent'; +import { linkButtonComponent } from './linkButtonComponent'; +import { plainTextComponent } from './plainTextComponent'; +import { rowComponent } from './rowComponent'; +import { spacerComponent } from './spacerComponent'; +import { textComponent } from './textComponent'; + +export const uiComponent = { + badge: badgeComponent, + container: containerComponent, + copyButton: copyButtonComponent, + divider: dividerComponent, + linkButton: linkButtonComponent, + plainText: plainTextComponent, + row: rowComponent, + spacer: spacerComponent, + text: textComponent, +}; diff --git a/src/ui-components/linkButtonComponent.ts b/src/ui-components/linkButtonComponent.ts new file mode 100644 index 0000000..5e8c2fb --- /dev/null +++ b/src/ui-components/linkButtonComponent.ts @@ -0,0 +1,13 @@ +import { type ComponentInput } from '../graphql/types'; + +/** + * Returns a ComponentInput which can be used with the API. + */ +export function linkButtonComponent(args: { label: string; url: string }): ComponentInput { + return { + componentLinkButton: { + linkButtonLabel: args.label, + linkButtonUrl: args.url, + }, + }; +} diff --git a/src/ui-components/plainTextComponent.ts b/src/ui-components/plainTextComponent.ts new file mode 100644 index 0000000..2e27742 --- /dev/null +++ b/src/ui-components/plainTextComponent.ts @@ -0,0 +1,48 @@ +import { + type ComponentInput, + ComponentPlainTextColor, + ComponentPlainTextSize, +} from '../graphql/types'; + +type Size = `${ComponentPlainTextSize}`; + +function textSizeToEnum(size?: Size): ComponentPlainTextSize | null { + if (!size) { + return null; + } + return ComponentPlainTextSize[size]; +} + +type Color = `${ComponentPlainTextColor}`; + +function textColorToEnum(color?: Color): ComponentPlainTextColor | null { + if (!color) { + return null; + } + + const map: Record = { + ERROR: ComponentPlainTextColor.Error, + MUTED: ComponentPlainTextColor.Muted, + NORMAL: ComponentPlainTextColor.Normal, + SUCCESS: ComponentPlainTextColor.Success, + WARNING: ComponentPlainTextColor.Warning, + }; + return map[color]; +} + +/** + * Returns a ComponentInput which can be used with the API. + */ +export function plainTextComponent(args: { + text: string; + size?: Size; + color?: Color; +}): ComponentInput { + return { + componentPlainText: { + plainText: args.text, + plainTextColor: textColorToEnum(args.color), + plainTextSize: textSizeToEnum(args.size), + }, + }; +} diff --git a/src/ui-components/rowComponent.ts b/src/ui-components/rowComponent.ts new file mode 100644 index 0000000..78e922b --- /dev/null +++ b/src/ui-components/rowComponent.ts @@ -0,0 +1,16 @@ +import { type ComponentInput } from '../graphql/types'; + +/** + * Returns a ComponentInput which can be used with the API. + */ +export function rowComponent(args: { + mainContent: ComponentInput[]; + asideContent: ComponentInput[]; +}): ComponentInput { + return { + componentRow: { + rowMainContent: args.mainContent, + rowAsideContent: args.asideContent, + }, + }; +} diff --git a/src/ui-components/spacerComponent.ts b/src/ui-components/spacerComponent.ts new file mode 100644 index 0000000..a4c163e --- /dev/null +++ b/src/ui-components/spacerComponent.ts @@ -0,0 +1,28 @@ +import { type ComponentInput, ComponentSpacerSize } from '../graphql/types'; + +type Size = `${ComponentSpacerSize}`; + +function spacingSizeToEnum(size?: Size): ComponentSpacerSize | null { + if (!size) { + return null; + } + const map: Record = { + XL: ComponentSpacerSize.Xl, + L: ComponentSpacerSize.L, + M: ComponentSpacerSize.M, + S: ComponentSpacerSize.S, + XS: ComponentSpacerSize.Xs, + }; + return map[size]; +} + +/** + * Returns a ComponentInput which can be used with the API. + */ +export function spacerComponent(args: { spacingSize?: Size }): ComponentInput { + return { + componentSpacer: { + spacerSize: spacingSizeToEnum(args.spacingSize), + }, + }; +} diff --git a/src/ui-components/textComponent.ts b/src/ui-components/textComponent.ts new file mode 100644 index 0000000..ca3f175 --- /dev/null +++ b/src/ui-components/textComponent.ts @@ -0,0 +1,40 @@ +import { type ComponentInput, ComponentTextColor, ComponentTextSize } from '../graphql/types'; + +type Size = `${ComponentTextSize}`; + +function textSizeToEnum(size?: Size): ComponentTextSize | null { + if (!size) { + return null; + } + return ComponentTextSize[size]; +} + +type Color = `${ComponentTextColor}`; + +function textColorToEnum(color?: Color): ComponentTextColor | null { + if (!color) { + return null; + } + + const map: Record = { + ERROR: ComponentTextColor.Error, + MUTED: ComponentTextColor.Muted, + NORMAL: ComponentTextColor.Normal, + SUCCESS: ComponentTextColor.Success, + WARNING: ComponentTextColor.Warning, + }; + return map[color]; +} + +/** + * Returns a ComponentInput which can be used with the API. + */ +export function textComponent(args: { text: string; size?: Size; color?: Color }): ComponentInput { + return { + componentText: { + text: args.text, + textColor: textColorToEnum(args.color), + textSize: textSizeToEnum(args.size), + }, + }; +} From 8f6c2193063f522367d1bf9db6b6619e7b45e5c4 Mon Sep 17 00:00:00 2001 From: Mathias Vagni Date: Thu, 14 Sep 2023 15:27:08 +0100 Subject: [PATCH 2/2] fix import --- src/ui-components/index.test.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ui-components/index.test.ts b/src/ui-components/index.test.ts index 9136d2d..b942fca 100644 --- a/src/ui-components/index.test.ts +++ b/src/ui-components/index.test.ts @@ -1,7 +1,6 @@ import { describe, expect, test } from 'vitest'; -import { ComponentTextColor, ComponentTextSize } from '../../dist'; -import { ComponentBadgeColor } from '../graphql/types'; +import { ComponentBadgeColor, ComponentTextColor, ComponentTextSize } from '../graphql/types'; import { uiComponent } from '.'; describe('ui components builder', () => {