浏览代码

Add files

master
Allan Crisostomo 3 年前
父节点
当前提交
1aa5486bea
共有 24 个文件被更改,包括 1338 次插入30 次删除
  1. +1
    -0
      packages/app-web/cypress.json
  2. +5
    -0
      packages/app-web/cypress/integration/login.ts
  3. +22
    -0
      packages/app-web/cypress/plugins/index.ts
  4. +25
    -0
      packages/app-web/cypress/support/commands.ts
  5. +20
    -0
      packages/app-web/cypress/support/index.ts
  6. +8
    -0
      packages/app-web/cypress/tsconfig.json
  7. +2
    -0
      packages/app-web/jest.config.js
  8. +1
    -0
      packages/app-web/next-env.d.ts
  9. +4
    -1
      packages/app-web/package.json
  10. +22
    -0
      packages/app-web/src/components/molecules/forms/ActionButton/index.test.tsx
  11. +89
    -0
      packages/app-web/src/components/molecules/forms/ActionButton/index.tsx
  12. +21
    -0
      packages/app-web/src/components/molecules/forms/TextArea/index.test.tsx
  13. +85
    -0
      packages/app-web/src/components/molecules/forms/TextArea/index.tsx
  14. +21
    -0
      packages/app-web/src/components/molecules/forms/TextInput/index.test.tsx
  15. +83
    -0
      packages/app-web/src/components/molecules/forms/TextInput/index.tsx
  16. +39
    -0
      packages/app-web/src/components/molecules/navigation/Link/index.tsx
  17. +37
    -0
      packages/app-web/src/components/organisms/forms/CreateRingtone/index.tsx
  18. +72
    -4
      packages/app-web/src/components/templates/CreateRingtone/index.tsx
  19. +11
    -0
      packages/app-web/src/models/Ringtone.ts
  20. +10
    -0
      packages/app-web/src/modules/ringtone/client.ts
  21. +28
    -1
      packages/app-web/src/pages/_app.tsx
  22. +6
    -1
      packages/app-web/src/pages/my/create/ringtone/index.tsx
  23. +3
    -2
      packages/app-web/tsconfig.test.json
  24. +723
    -21
      packages/app-web/yarn.lock

+ 1
- 0
packages/app-web/cypress.json 查看文件

@@ -0,0 +1 @@
{}

+ 5
- 0
packages/app-web/cypress/integration/login.ts 查看文件

@@ -0,0 +1,5 @@
describe('login', () => {
it('should log the current user in', () => {
cy.visit('http://localhost:3000')
})
})

+ 22
- 0
packages/app-web/cypress/plugins/index.ts 查看文件

@@ -0,0 +1,22 @@
/// <reference types="cypress" />
// ***********************************************************
// This example plugins/index.js can be used to load plugins
//
// You can change the location of this file or turn off loading
// the plugins file with the 'pluginsFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/plugins-guide
// ***********************************************************

// This function is called when a project is opened or re-opened (e.g. due to
// the project's config changing)

/**
* @type {Cypress.PluginConfig}
*/
// eslint-disable-next-line no-unused-vars
export default (on, config) => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
}

+ 25
- 0
packages/app-web/cypress/support/commands.ts 查看文件

@@ -0,0 +1,25 @@
// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add('login', (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })

+ 20
- 0
packages/app-web/cypress/support/index.ts 查看文件

@@ -0,0 +1,20 @@
// ***********************************************************
// This example support/index.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************

// Import commands.js using ES2015 syntax:
import './commands'

// Alternatively you can use CommonJS syntax:
// require('./commands')

+ 8
- 0
packages/app-web/cypress/tsconfig.json 查看文件

@@ -0,0 +1,8 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["es5", "dom"],
"types": ["cypress"]
},
"include": ["**/*.ts"]
}

+ 2
- 0
packages/app-web/jest.config.js 查看文件

@@ -8,5 +8,7 @@ module.exports = {
},
collectCoverageFrom: [
'src/components/**/*.{jsx,tsx}',
'src/modules/**/*.{js,ts}',
'src/utils/**/*.{js,ts}',
]
};

+ 1
- 0
packages/app-web/next-env.d.ts 查看文件

@@ -1,2 +1,3 @@
/// <reference types="next" />
/// <reference types="next/types/global" />
/// <reference types="jest" />

+ 4
- 1
packages/app-web/package.json 查看文件

@@ -6,10 +6,12 @@
"dev": "next dev",
"build": "next build",
"start": "next start",
"test": "jest"
"test": "jest",
"e2e": "cypress"
},
"dependencies": {
"@tesseract-design/viewfinder": "^0.1.1",
"@theoryofnekomata/formxtr": "^0.1.2",
"next": "10.2.0",
"react": "17.0.2",
"react-dom": "17.0.2",
@@ -21,6 +23,7 @@
"@types/node": "^15.0.2",
"@types/react": "^17.0.5",
"@types/styled-components": "^5.1.9",
"cypress": "^7.3.0",
"jest": "^26.6.3",
"ts-jest": "^26.5.6",
"typescript": "^4.2.4"


+ 22
- 0
packages/app-web/src/components/molecules/forms/ActionButton/index.test.tsx 查看文件

@@ -0,0 +1,22 @@
import {cleanup, render} from '@testing-library/react'
import ActionButton from '.'

describe('button component for triggering actions', () => {
afterEach(() => {
cleanup()
})

it('should render a button element with a no-op action', () => {
const result = render(<ActionButton />)
const input = result.queryByRole('button')
expect(input).not.toBeNull()
})

describe.each(['button', 'reset', 'submit'] as const)('on %p action', (type) => {
it('should render a button element with a submit action', () => {
const result = render(<ActionButton type={type} />)
const input = result.queryByRole('button') as HTMLButtonElement
expect(input.type).toBe(type)
})
})
})

+ 89
- 0
packages/app-web/src/components/molecules/forms/ActionButton/index.tsx 查看文件

@@ -0,0 +1,89 @@
import styled from 'styled-components';
import {FC, ReactChild} from 'react';

const Base = styled('div')({
height: '3rem',
borderRadius: '0.25rem',
overflow: 'hidden',
position: 'relative',
'::before': {
content: "''",
borderWidth: 1,
borderStyle: 'solid',
borderColor: 'inherit',
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
borderRadius: 'inherit',
boxSizing: 'border-box',
},
})

const ClickArea = styled('button')({
display: 'block',
width: '100%',
height: '100%',
margin: 0,
padding: '0 1rem',
boxSizing: 'border-box',
font: 'inherit',
border: 0,
backgroundColor: 'transparent',
color: 'inherit',
outline: 0,
textTransform: 'uppercase',
fontWeight: 'bolder',
position: 'relative',
})

const VARIANTS = {
default: {
backgroundColor: 'var(--color-bg, white)',
borderColor: 'var(--color-fg, black)',
color: 'var(--color-fg, black)',
},
primary: {
backgroundColor: 'var(--color-fg, black)',
borderColor: 'var(--color-fg, black)',
color: 'var(--color-bg, white)',
},
}

type Props = {
children?: ReactChild,
className?: string,
type?: 'button' | 'reset' | 'submit',
block?: boolean,
variant?: keyof typeof VARIANTS,
}

const ActionButton: FC<Props> = ({
children,
className,
type = 'button',
block,
variant = 'default',
...etcProps
}) => {
return (
<Base
className={className}
style={{
...VARIANTS[variant],
display: block ? 'block' : 'inline-block',
width: block ? '100%' : undefined,
}}
>
<ClickArea
{...etcProps}
type={type}
>
{children}
</ClickArea>
</Base>
)
}

export default ActionButton

+ 21
- 0
packages/app-web/src/components/molecules/forms/TextArea/index.test.tsx 查看文件

@@ -0,0 +1,21 @@
import {render, cleanup} from '@testing-library/react'
import TextInput from '.'

describe('single-line text input component', () => {
afterEach(() => {
cleanup()
})

it('should contain a text input element', () => {
const result = render(<TextInput label="" name="" />)
const input = result.queryByRole('textbox')
expect(input).not.toBeNull()
})

it('should acquire a descriptive label', () => {
const label = 'foo'
const result = render(<TextInput label={label} name="" />)
const input = result.queryByLabelText(label)
expect(input).not.toBeNull()
})
})

+ 85
- 0
packages/app-web/src/components/molecules/forms/TextArea/index.tsx 查看文件

@@ -0,0 +1,85 @@
import {FC} from 'react'
import styled from 'styled-components'

const Base = styled('div')({
height: '3rem',
borderRadius: '0.25rem',
overflow: 'hidden',
position: 'relative',
backgroundColor: 'var(--color-bg, white)',
'::before': {
content: "''",
borderWidth: 1,
borderStyle: 'solid',
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
borderRadius: 'inherit',
boxSizing: 'border-box',
},
})

const ClickArea = styled('label')({
position: 'relative',
height: '100%',
})

const Label = styled('span')({
position: 'absolute',
left: -999999,
})

const Input = styled('textarea')({
display: 'block',
width: '100%',
height: '100%',
margin: 0,
padding: '0 1rem',
boxSizing: 'border-box',
font: 'inherit',
border: 0,
backgroundColor: 'transparent',
color: 'inherit',
outline: 0,
})

type Props = {
label: string,
name: string,
className?: string,
block?: boolean,
placeholder?: string,
}

const TextArea: FC<Props> = ({
label,
className,
block,
...etcProps
}) => {
return (
<Base
className={className}
style={{
display: block ? 'block' : 'inline-block',
width: block ? '100%' : undefined,
}}
>
<ClickArea>
<Label>
{label}
</Label>
<Input
{...etcProps}
style={{
resize: block ? 'vertical' : undefined,
}}
/>
</ClickArea>
</Base>
)
}

export default TextArea

+ 21
- 0
packages/app-web/src/components/molecules/forms/TextInput/index.test.tsx 查看文件

@@ -0,0 +1,21 @@
import {render, cleanup} from '@testing-library/react'
import TextInput from '.'

describe('single-line text input component', () => {
afterEach(() => {
cleanup()
})

it('should contain a text input element', () => {
const result = render(<TextInput label="" name="" />)
const input = result.queryByRole('textbox')
expect(input).not.toBeNull()
})

it('should acquire a descriptive label', () => {
const label = 'foo'
const result = render(<TextInput label={label} name="" />)
const input = result.queryByLabelText(label)
expect(input).not.toBeNull()
})
})

+ 83
- 0
packages/app-web/src/components/molecules/forms/TextInput/index.tsx 查看文件

@@ -0,0 +1,83 @@
import {FC} from 'react'
import styled from 'styled-components'

const Base = styled('div')({
height: '3rem',
borderRadius: '0.25rem',
overflow: 'hidden',
position: 'relative',
backgroundColor: 'var(--color-bg, white)',
'::before': {
content: "''",
borderWidth: 1,
borderStyle: 'solid',
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
borderRadius: 'inherit',
boxSizing: 'border-box',
},
})

const ClickArea = styled('label')({
position: 'relative',
height: '100%',
})

const Label = styled('span')({
position: 'absolute',
left: -999999,
})

const Input = styled('input')({
display: 'block',
width: '100%',
height: '100%',
margin: 0,
padding: '0 1rem',
boxSizing: 'border-box',
font: 'inherit',
border: 0,
backgroundColor: 'transparent',
color: 'inherit',
outline: 0,
})

type Props = {
label: string,
name: string,
className?: string,
block?: boolean,
placeholder?: string,
type?: 'email' | 'url' | 'text' | 'tel',
}

const TextInput: FC<Props> = ({
label,
className,
block,
...etcProps
}) => {
return (
<Base
className={className}
style={{
display: block ? 'block' : 'inline-block',
width: block ? '100%' : undefined,
}}
>
<ClickArea>
<Label>
{label}
</Label>
<Input
{...etcProps}
/>
</ClickArea>
</Base>
)
}

export default TextInput

+ 39
- 0
packages/app-web/src/components/molecules/navigation/Link/index.tsx 查看文件

@@ -0,0 +1,39 @@
import * as React from 'react'
import NextLink from 'next/link'
import {UrlObject} from 'url'

type Props = {
href: UrlObject,
as?: UrlObject,
prefetch?: boolean,
replace?: boolean,
shallow?: boolean,
component?: React.ElementType,
}

const Link: React.FC<Props> = ({
href,
as,
prefetch,
replace,
shallow,
component: Component = 'a',
...etcProps
}) => {
return (
<NextLink
href={href}
as={as}
passHref
replace={replace}
shallow={shallow}
prefetch={prefetch}
>
<Component
{...etcProps}
/>
</NextLink>
)
}

export default Link

+ 37
- 0
packages/app-web/src/components/organisms/forms/CreateRingtone/index.tsx 查看文件

@@ -0,0 +1,37 @@
import {FC, FormEventHandler} from 'react'
import styled from 'styled-components'
import TextInput from '../../../molecules/forms/TextInput'
import TextArea from '../../../molecules/forms/TextArea'
import ActionButton from '../../../molecules/forms/ActionButton'

const Form = styled('form')({
display: 'grid',
gap: '1rem',
})

type Props = {
onSubmit?: FormEventHandler
}

const CreateRingtoneForm: FC<Props> = ({
onSubmit,
}) => {
return (
<Form
onSubmit={onSubmit}
method="post"
action="/api/a/create/ringtone"
>
<TextInput label="Name" name="name" block />
<TextArea label="Data" name="data" block />
<ActionButton
type="submit"
block
>
Post
</ActionButton>
</Form>
)
}

export default CreateRingtoneForm

+ 72
- 4
packages/app-web/src/components/templates/CreateRingtone/index.tsx 查看文件

@@ -1,8 +1,76 @@
const CreateRingtoneTemplate = () => {
import {FC, FormEventHandler} from 'react'
import { LeftSidebarWithMenu } from '@tesseract-design/viewfinder'
import CreateRingtoneForm from '../../organisms/forms/CreateRingtone'
import Link from '../../molecules/navigation/Link'

type Props = {
onSubmit?: FormEventHandler
}

const CreateRingtoneTemplate: FC<Props> = ({
onSubmit,
}) => {
return (
<div>
CreateRingtone
</div>
<LeftSidebarWithMenu.Layout
linkComponent={({ url, icon, label, }) => (
<Link
href={url}
>
<LeftSidebarWithMenu.SidebarMenuContainer>
<LeftSidebarWithMenu.SidebarMenuItemIcon>
{icon}
</LeftSidebarWithMenu.SidebarMenuItemIcon>
{label}
</LeftSidebarWithMenu.SidebarMenuContainer>
</Link>
)}
moreLinkMenuItem={{
label: 'More',
icon: 'M',
url: {},
}}
moreLinkComponent={({ url, icon, label, }) => (
<Link
href={url}
>
<LeftSidebarWithMenu.MoreSidebarMenuContainer>
<LeftSidebarWithMenu.MoreSidebarMenuItemIcon>
{icon}
</LeftSidebarWithMenu.MoreSidebarMenuItemIcon>
{label}
</LeftSidebarWithMenu.MoreSidebarMenuContainer>
</Link>
)}
sidebarMenuItems={[
{
id: 'browse',
label: 'Browse',
icon: 'B',
url: {
pathname: '/ringtones',
},
},
{
id: 'compose',
label: 'Compose',
icon: 'C',
url: {
pathname: '/my/create/ringtone',
},
},
]}
sidebarMain={
<LeftSidebarWithMenu.SidebarMainContainer>
Hi
</LeftSidebarWithMenu.SidebarMainContainer>
}
>
<LeftSidebarWithMenu.ContentContainer>
<CreateRingtoneForm
onSubmit={onSubmit}
/>
</LeftSidebarWithMenu.ContentContainer>
</LeftSidebarWithMenu.Layout>
)
}



+ 11
- 0
packages/app-web/src/models/Ringtone.ts 查看文件

@@ -0,0 +1,11 @@
export default class Ringtone {
name: string

data: string

createdAt: Date

updatedAt: Date

deletedAt?: Date
}

+ 10
- 0
packages/app-web/src/modules/ringtone/client.ts 查看文件

@@ -0,0 +1,10 @@
import getFormValues from '@theoryofnekomata/formxtr'
import {FormEvent} from 'react'

export default class RingtoneClient {
async save(e: FormEvent & { submitter: HTMLInputElement | HTMLButtonElement }) {
e.preventDefault()
const values = getFormValues(e.target as HTMLFormElement, e.submitter)
console.log(values)
}
}

+ 28
- 1
packages/app-web/src/pages/_app.tsx 查看文件

@@ -1,5 +1,32 @@
import { createGlobalStyle } from 'styled-components'

const GlobalStyle = createGlobalStyle({
':root': {
'--color-bg': 'white',
'--color-fg': 'black',
color: 'var(--color-fg, black)',
backgroundColor: 'var(--color-bg, white)',
},
'body': {
margin: 0,
},
'@media (prefers-color-scheme: dark)': {
':root': {
'--color-bg': 'black',
'--color-fg': 'white',
color: 'var(--color-fg, white)',
backgroundColor: 'var(--color-bg, black)',
},
},
})

const MyApp = ({Component, pageProps}) => {
return <Component {...pageProps} />
return (
<>
<GlobalStyle/>
<Component {...pageProps} />
</>
)
}

export default MyApp;

+ 6
- 1
packages/app-web/src/pages/my/create/ringtone/index.tsx 查看文件

@@ -1,11 +1,16 @@
import {NextPage} from 'next'
import {useRef} from 'react'
import CreateRingtoneTemplate from '../../../../components/templates/CreateRingtone'
import RingtoneClient from '../../../../modules/ringtone/client'

type Props = {}

const MyCreateRingtonePage: NextPage<Props> = () => {
const ringtoneClient = useRef(new RingtoneClient())
return (
<CreateRingtoneTemplate />
<CreateRingtoneTemplate
onSubmit={ringtoneClient.current.save}
/>
)
}



+ 3
- 2
packages/app-web/tsconfig.test.json 查看文件

@@ -1,6 +1,7 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"jsx": "react-jsx"
}
"jsx": "react-jsx",
"types": ["jest"]
},
}

+ 723
- 21
packages/app-web/yarn.lock
文件差异内容过多而无法显示
查看文件


正在加载...
取消
保存