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

feat: input and label components #70

Merged
merged 29 commits into from
Jan 16, 2024
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
7f630c5
Add tailwind config and update webpack config
raducristianpopa Dec 20, 2023
ef98ba8
Add colors variables
raducristianpopa Dec 20, 2023
7fe2866
Add popup variables
raducristianpopa Dec 20, 2023
ec3182f
Move PostCSS config to webpack config
raducristianpopa Dec 20, 2023
c0fad38
Add initial button component
raducristianpopa Dec 21, 2023
a4c7f9c
Update jest config and setup files
raducristianpopa Dec 21, 2023
cda31f2
Add initial button tests
raducristianpopa Dec 21, 2023
a4e9bf2
Merge branch 'main' into rp--button-component
raducristianpopa Dec 21, 2023
2381870
Fix error background
raducristianpopa Dec 21, 2023
061ff75
Add destructive variant
raducristianpopa Dec 21, 2023
30cb366
Update button tests
raducristianpopa Dec 21, 2023
89b89ac
Add test job to workflows and update CI test script
raducristianpopa Dec 21, 2023
3c0f858
Fix test name
raducristianpopa Dec 21, 2023
9e59d90
Input and label components
ionutanin Dec 28, 2023
d148975
Update src/components/input.tsx
ionutanin Jan 8, 2024
f263e4b
Update src/components/label.tsx
ionutanin Jan 8, 2024
34461a2
Update src/components/label.tsx
ionutanin Jan 8, 2024
a568508
Update src/components/input.tsx
ionutanin Jan 8, 2024
ba41482
Update src/components/__tests__/input.test.tsx
ionutanin Jan 8, 2024
1e78553
Update src/components/input.tsx
ionutanin Jan 8, 2024
8b268ff
Update src/components/input.tsx
ionutanin Jan 8, 2024
22ff46c
Update src/components/input.tsx
ionutanin Jan 8, 2024
d4060ca
Merge branch 'main' of github.com:interledger/web-monetization-extens…
ionutanin Jan 10, 2024
a90bb5a
Merge branch 'rp--input-component' of github.com:interledger/web-mone…
ionutanin Jan 10, 2024
f2d560a
add input error message, add unit tests, updates based on code review
ionutanin Jan 10, 2024
21596ae
fix error message position
ionutanin Jan 10, 2024
38e7871
remove undefined fallback
ionutanin Jan 11, 2024
c6cda6f
remove absolute style on error message and change tag
ionutanin Jan 13, 2024
7a0683f
add classes on error message
ionutanin Jan 16, 2024
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
55 changes: 55 additions & 0 deletions src/components/__tests__/input.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { render } from '@testing-library/react'
import React from 'react'

import { Input } from '@/components/input'

describe('Input', () => {
it('should default to `type="text"`', () => {
const { queryByLabelText } = render(<Input aria-label="test input" />)

expect(queryByLabelText('test input')).toBeInTheDocument()
expect(queryByLabelText('test input')).toHaveAttribute('type', 'text')
})

it('should not have the `disabled` attribute and `aria-disabled="false"` if `loading` is false', () => {
const { queryByLabelText } = render(<Input aria-label="test input" />)

expect(queryByLabelText('test input')).toBeInTheDocument()
expect(queryByLabelText('test input')).not.toHaveAttribute('disabled')
expect(queryByLabelText('test input')).toHaveAttribute('aria-disabled', 'false')
expect(queryByLabelText('test input')).not.toBeDisabled()
})

it('should have the `border-base` class by default', () => {
const { queryByLabelText } = render(<Input aria-label="test input" />)

expect(queryByLabelText('test input')).toBeInTheDocument()
expect(queryByLabelText('test input')).toHaveClass('border-base')
})

it('should have the `pl-12` class when the `icon` variant is passed', () => {
const { queryByLabelText } = render(<Input aria-label="test input" icon={<div />} />)

expect(queryByLabelText('test input')).toBeInTheDocument()
expect(queryByLabelText('test input')).toHaveClass('pl-12')
})

it('should have the `bg-disabled` and `border-transparent` classes when the `disabled` variant is passed', () => {
const { queryByLabelText } = render(<Input aria-label="test input" disabled />)

expect(queryByLabelText('test input')).toBeInTheDocument()
expect(queryByLabelText('test input')).toHaveClass('bg-disabled')
expect(queryByLabelText('test input')).toHaveClass('border-transparent')
})

it('should have the `aria-invalid` and `aria-describedby` attributes if errorMessage is present', () => {
const { queryByLabelText, queryByText } = render(
<Input aria-label="test input" errorMessage="some error" />,
)

expect(queryByLabelText('test input')).toBeInTheDocument()
expect(queryByLabelText('test input')).toHaveAttribute('aria-invalid')
expect(queryByLabelText('test input')).toHaveAttribute('aria-describedby')
expect(queryByText('some error')).toBeInTheDocument()
})
})
32 changes: 32 additions & 0 deletions src/components/icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,35 @@ export const Spinner = (props: React.SVGProps<SVGSVGElement>) => {
</svg>
)
}

export const DollarSign = (props: React.SVGProps<SVGSVGElement>) => {
return (
<svg
width="24"
height="24"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}>
<g id="attach_money">
<mask
id="mask0_140_3168"
style={{ maskType: 'alpha' }}
maskUnits="userSpaceOnUse"
x="0"
y="0"
width="24"
height="24">
<rect id="Bounding box" width="24" height="24" fill="#D9D9D9" />
</mask>
<g mask="url(#mask0_140_3168)">
<path
id="attach_money_2"
d="M11.0252 21V18.85C10.1419 18.65 9.37953 18.2667 8.7382 17.7C8.0962 17.1333 7.6252 16.3333 7.3252 15.3L9.1752 14.55C9.4252 15.35 9.7962 15.9583 10.2882 16.375C10.7795 16.7917 11.4252 17 12.2252 17C12.9085 17 13.4879 16.846 13.9632 16.538C14.4379 16.2293 14.6752 15.75 14.6752 15.1C14.6752 14.5167 14.4919 14.054 14.1252 13.712C13.7585 13.3707 12.9085 12.9833 11.5752 12.55C10.1419 12.1 9.15853 11.5627 8.6252 10.938C8.09186 10.3127 7.8252 9.55 7.8252 8.65C7.8252 7.56667 8.1752 6.725 8.8752 6.125C9.5752 5.525 10.2919 5.18333 11.0252 5.1V3H13.0252V5.1C13.8585 5.23333 14.5462 5.53733 15.0882 6.012C15.6295 6.48733 16.0252 7.06667 16.2752 7.75L14.4252 8.55C14.2252 8.01667 13.9419 7.61667 13.5752 7.35C13.2085 7.08333 12.7085 6.95 12.0752 6.95C11.3419 6.95 10.7835 7.11267 10.4002 7.438C10.0169 7.76267 9.8252 8.16667 9.8252 8.65C9.8252 9.2 10.0752 9.63333 10.5752 9.95C11.0752 10.2667 11.9419 10.6 13.1752 10.95C14.3252 11.2833 15.1962 11.8123 15.7882 12.537C16.3795 13.2623 16.6752 14.1 16.6752 15.05C16.6752 16.2333 16.3252 17.1333 15.6252 17.75C14.9252 18.3667 14.0585 18.75 13.0252 18.9V21H11.0252Z"
fill="#475569"
/>
</g>
</g>
</svg>
)
}
60 changes: 60 additions & 0 deletions src/components/input.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { type VariantProps, cva } from 'class-variance-authority'
import React, { forwardRef } from 'react'

import { cn } from '@/utils/cn'

const inputVariants = cva(
[
'w-full h-14 rounded-xl border border-2 px-4 text-base text-medium',
'focus:outline-none focus:border-focus',
'placeholder-disabled',
],

{
variants: {
variant: {
default: 'border-base',
},
disabled: {
true: 'bg-disabled border-transparent',
},
},
defaultVariants: {
variant: 'default',
},
},
)

export interface InputProps
extends VariantProps<typeof inputVariants>,
React.InputHTMLAttributes<HTMLInputElement> {
errorMessage?: string
disabled?: boolean
icon?: React.ReactNode
}

export const Input = forwardRef<HTMLInputElement, InputProps>(function Input(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we pass an error message we need to:

  • render the error message below the input
  • add the aria-invalid and aria-describedby attributes to the input

Additionally we can use the label component directly, by adding a label prop:

<Input label="Amount" {...restOfProps} />

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we must use it separately because there is a situation when we have also a link besides the actualy label (as per graphic)
e.g. Label link

{ type = 'text', icon, errorMessage, disabled, className, ...props },
ref,
) {
return (
<div className="relative">
{icon && <div className="absolute left-4 top-4">{icon}</div>}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we satisfied with the absolute positioning for the icon or should we inline it with the input?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's fine for now, I don't see yet an advantage of using it inline 🤔

<input
ref={ref}
type={type}
className={cn(inputVariants({ disabled }), icon && 'pl-12', className)}
disabled={disabled ?? false}
aria-disabled={disabled ?? false}
aria-invalid={!!errorMessage || undefined}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that !!errorMessage should be sufficient.

aria-describedby={errorMessage}
{...props}
/>
{errorMessage && (
<div className="absolute top-full left-0 right-0 text-error text-sm px-2">
{errorMessage}
</div>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not set absolute position to the error message. A simple p tag should work.

)}
</div>
)
})
23 changes: 23 additions & 0 deletions src/components/label.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { type VariantProps, cva } from 'class-variance-authority'
import React, { forwardRef } from 'react'

import { cn } from '@/utils/cn'

const labelVariants = cva('text-medium font-medium leading-6 px-2 flex items-center gap-2')

export interface LabelProps
extends VariantProps<typeof labelVariants>,
React.LabelHTMLAttributes<HTMLLabelElement> {
children: React.ReactNode
}

export const Label = forwardRef<HTMLLabelElement, LabelProps>(function Label(
{ className, children, ...props },
ref,
) {
return (
<label ref={ref} className={cn(labelVariants(), className)} {...props}>
{children}
</label>
)
})
3 changes: 2 additions & 1 deletion src/popup/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
/* Border colors */
--border-base: 203 213 225;
--border-focus: 59 130 246;

--border-error: 220 38 38;

/* Popup */
--popup-width: 448px;
--popup-height: 559px;
Expand Down
1 change: 1 addition & 0 deletions tailwind.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ module.exports = {
base: 'rgb(var(--border-base) / <alpha-value>)',
popup: 'rgb(var(--border-popup) / <alpha-value>)',
focus: 'rgb(var(--border-focus) / <alpha-value>)',
error: 'rgb(var(--border-error) / <alpha-value>)',
},
},
},
Expand Down