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

users: use generic destinations for user CM form #3663

Merged
merged 5 commits into from
Feb 9, 2024
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
23 changes: 11 additions & 12 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,18 +48,17 @@
"@mui/system": "5.15.6",
"@mui/x-data-grid": "6.19.3",
"@playwright/test": "1.41.2",
"@storybook/addon-essentials": "7.6.7",
"@storybook/addon-interactions": "7.6.7",
"@storybook/addon-links": "7.6.7",
"@storybook/addons": "7.6.7",
"@storybook/blocks": "7.6.10",
"@storybook/jest": "0.2.3",
"@storybook/preview-api": "7.6.7",
"@storybook/react": "7.6.7",
"@storybook/react-vite": "7.6.7",
"@storybook/addon-essentials": "7.6.13",
"@storybook/addon-interactions": "7.6.13",
"@storybook/addon-links": "7.6.13",
"@storybook/addons": "7.6.13",
"@storybook/blocks": "7.6.13",
"@storybook/preview-api": "7.6.13",
"@storybook/react": "7.6.13",
"@storybook/react-vite": "7.6.13",
"@storybook/test": "7.6.13",
"@storybook/test-runner": "0.16.0",
"@storybook/testing-library": "0.2.2",
"@storybook/types": "7.6.7",
"@storybook/types": "7.6.13",
"@types/chance": "1.1.4",
"@types/diff": "5.0.8",
"@types/glob": "8.1.0",
Expand Down Expand Up @@ -134,7 +133,7 @@
"remark-breaks": "4.0.0",
"remark-gfm": "4.0.0",
"semver": "7.5.4",
"storybook": "7.6.7",
"storybook": "7.6.13",
"storybook-addon-mock": "4.3.0",
"stylelint": "15.11.0",
"stylelint-config-standard": "34.0.0",
Expand Down
1 change: 1 addition & 0 deletions web/src/app/forms/FormField.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ FormField.propTypes = {

multiple: p.bool,

destType: p.string,
options: p.arrayOf(
p.shape({
label: p.string,
Expand Down
3 changes: 1 addition & 2 deletions web/src/app/selection/DestinationField.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import React from 'react'
import type { Meta, StoryObj } from '@storybook/react'
import DestinationField from './DestinationField'
import { expect } from '@storybook/jest'
import { within } from '@storybook/testing-library'
import { expect, within } from '@storybook/test'
import { handleDefaultConfig } from '../storybook/graphql'
import { useArgs } from '@storybook/preview-api'
import { FieldValueInput } from '../../schema'
Expand Down
3 changes: 1 addition & 2 deletions web/src/app/selection/DestinationInputDirect.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import React from 'react'
import type { Meta, StoryObj } from '@storybook/react'
import DestinationInputDirect from './DestinationInputDirect'
import { expect } from '@storybook/jest'
import { within, userEvent } from '@storybook/testing-library'
import { expect, within, userEvent } from '@storybook/test'
import { handleDefaultConfig } from '../storybook/graphql'
import { HttpResponse, graphql } from 'msw'
import { useArgs } from '@storybook/preview-api'
Expand Down
2 changes: 1 addition & 1 deletion web/src/app/selection/DestinationInputDirect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ export default function DestinationInputDirect(
}

// add live validation icon to the right of the textfield as an endAdornment
if (adorn && props.value === debouncedValue) {
if (adorn && props.value === debouncedValue && !props.disabled) {
iprops = {
endAdornment: <InputAdornment position='end'>{adorn}</InputAdornment>,
...iprops,
Expand Down
3 changes: 1 addition & 2 deletions web/src/app/selection/DestinationSearchSelect.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import React from 'react'
import type { Meta, StoryObj } from '@storybook/react'
import DestinationSearchSelect from './DestinationSearchSelect'
import { expect } from '@storybook/jest'
import { userEvent, within } from '@storybook/testing-library'
import { expect, userEvent, within } from '@storybook/test'
import { handleDefaultConfig } from '../storybook/graphql'
import { HttpResponse, graphql } from 'msw'
import { useArgs } from '@storybook/preview-api'
Expand Down
58 changes: 57 additions & 1 deletion web/src/app/storybook/defaultDestTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export const destTypes: DestinationTypeInfo[] = [
isSchedOnCallNotify: false,
iconURL: '',
iconAltText: '',
supportsStatusUpdates: false,
supportsStatusUpdates: true,
statusUpdatesRequired: false,
requiredFields: [
{
Expand Down Expand Up @@ -109,4 +109,60 @@ export const destTypes: DestinationTypeInfo[] = [
},
],
},
{
type: 'supports-status',
name: 'Single Field Destination Type',
enabled: true,
disabledMessage: 'Single field destination type must be configured.',
userDisclaimer: '',
isContactMethod: true,
isEPTarget: false,
isSchedOnCallNotify: false,
iconURL: '',
iconAltText: '',
supportsStatusUpdates: true,
statusUpdatesRequired: false,
requiredFields: [
{
fieldID: 'phone-number',
labelSingular: 'Phone Number',
labelPlural: 'Phone Numbers',
hint: 'Include country code e.g. +1 (USA), +91 (India), +44 (UK)',
hintURL: '',
placeholderText: '11235550123',
prefix: '+',
inputType: 'tel',
isSearchSelectable: false,
supportsValidation: true,
},
],
},
{
type: 'required-status',
name: 'Single Field Destination Type',
enabled: true,
disabledMessage: 'Single field destination type must be configured.',
userDisclaimer: '',
isContactMethod: true,
isEPTarget: false,
isSchedOnCallNotify: false,
iconURL: '',
iconAltText: '',
supportsStatusUpdates: false,
statusUpdatesRequired: true,
requiredFields: [
{
fieldID: 'phone-number',
labelSingular: 'Phone Number',
labelPlural: 'Phone Numbers',
hint: 'Include country code e.g. +1 (USA), +91 (India), +44 (UK)',
hintURL: '',
placeholderText: '11235550123',
prefix: '+',
inputType: 'tel',
isSearchSelectable: false,
supportsValidation: true,
},
],
},
]
219 changes: 219 additions & 0 deletions web/src/app/users/UserContactMethodFormDest.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
import React from 'react'
import type { Meta, StoryObj } from '@storybook/react'
import UserContactMethodFormDest, { Value } from './UserContactMethodFormDest'
import { expect, within, userEvent, waitFor } from '@storybook/test'
import { handleDefaultConfig } from '../storybook/graphql'
import { useArgs } from '@storybook/preview-api'
import { HttpResponse, graphql } from 'msw'

const meta = {
title: 'users/UserContactMethodFormDest',
component: UserContactMethodFormDest,
tags: ['autodocs'],
parameters: {
msw: {
handlers: [
handleDefaultConfig,
graphql.query('ValidateDestination', ({ variables: vars }) => {
return HttpResponse.json({
data: {
destinationFieldValidate: vars.input.value === '+15555555555',
},
})
}),
],
},
},
render: function Component(args) {
const [, setArgs] = useArgs()
const onChange = (newValue: Value): void => {
if (args.onChange) args.onChange(newValue)
setArgs({ value: newValue })
}
return <UserContactMethodFormDest {...args} onChange={onChange} />
},
} satisfies Meta<typeof UserContactMethodFormDest>

export default meta
type Story = StoryObj<typeof meta>

export const SupportStatusUpdates: Story = {
args: {
value: {
name: 'supports status',
dest: {
type: 'supports-status',
values: [
{
fieldID: 'phone-number',
value: '+15555555555',
},
],
},
statusUpdates: false,
},
disabled: false,
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement)
// ensure status updates checkbox is clickable

await expect(
await canvas.findByLabelText('Send alert status updates'),
).not.toBeDisabled()
},
}

export const RequiredStatusUpdates: Story = {
args: {
value: {
name: 'required status',
dest: {
type: 'required-status',
values: [
{
fieldID: 'phone-number',
value: '+15555555555',
},
],
},
statusUpdates: false,
},
disabled: false,
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement)
// ensure status updates checkbox is not clickable

await expect(
await canvas.findByLabelText(
'Send alert status updates (cannot be disabled for this type)',
),
).toBeDisabled()
},
}

export const ErrorSingleField: Story = {
args: {
value: {
name: '-notvalid',
dest: {
type: 'single-field',
values: [
{
fieldID: 'phone-number',
value: '+',
},
],
},
statusUpdates: false,
},
disabled: false,
errors: [
{
field: 'name',
message: 'must begin with a letter',
name: 'FieldError',
path: [],
details: {},
},
],
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement)
await userEvent.type(await canvas.findByLabelText('Phone Number'), '123')

// ensure errors are shown
await expect(
await canvas.findByText('Must begin with a letter'),
).toBeVisible()
await waitFor(async function CloseIcon() {
await expect(await canvas.findByTestId('CloseIcon')).toBeVisible()
})
},
}

export const ErrorMultiField: Story = {
args: {
value: {
name: '-notvalid',
dest: {
type: 'triple-field',
values: [
{
fieldID: 'first-field',
value: '+',
},
{
fieldID: 'second-field',
value: 'notAnEmail',
},
{
fieldID: 'third-field',
value: '-',
},
],
},
statusUpdates: false,
},
disabled: false,
errors: [
{
field: 'name',
message: 'must begin with a letter',
name: 'FieldError',
path: [],
details: {},
},
],
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement)
await userEvent.type(await canvas.findByLabelText('First Item'), '123')

// ensure errors are shown
await expect(
await canvas.findByText('Must begin with a letter'),
).toBeVisible()

await waitFor(async function ThreeCloseIcons() {
await expect(await canvas.findAllByTestId('CloseIcon')).toHaveLength(3)
})
},
}

export const Disabled: Story = {
args: {
value: {
name: 'disabled dest',
dest: {
type: 'triple-field',
values: [],
},
statusUpdates: false,
},
disabled: true,
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement)

const combo = await canvas.findByRole('combobox')

// get it's input field sibling (combo is a dom element)
const input = combo.parentElement?.querySelector('input')
await expect(input).toBeDisabled()

await expect(
await canvas.findByPlaceholderText('11235550123'),
).toBeDisabled()
await expect(
await canvas.findByPlaceholderText('foobar@example.com'),
).toBeDisabled()
await expect(
await canvas.findByPlaceholderText('slack user ID'),
).toBeDisabled()
await expect(
await canvas.findByLabelText('Send alert status updates'),
).toBeDisabled()
},
}
Loading