Skip to content

Commit

Permalink
Update all input element styles (#181)
Browse files Browse the repository at this point in the history
  • Loading branch information
carbonrobot authored Nov 14, 2023
1 parent e9e14ae commit 36b5e70
Show file tree
Hide file tree
Showing 25 changed files with 206 additions and 202 deletions.
5 changes: 5 additions & 0 deletions .changeset/fresh-books-boil.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@envyjs/webui': patch
---

Update input element styles
2 changes: 1 addition & 1 deletion packages/webui/src/components/Authorization.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ jest.mock('@/components', () => ({
Code: function ({ children, ...props }: any) {
return <div {...props}>{children}</div>;
},
IconButton: function ({ short, Icon, ...safeProps }: any) {
Button: function ({ short, Icon, ...safeProps }: any) {
return <button {...safeProps} />;
},
CodeDisplay: function ({ data, contentType, ...props }: any) {
Expand Down
27 changes: 15 additions & 12 deletions packages/webui/src/components/Authorization.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ChevronDown, ChevronUp, Code2, MoreHorizontal } from 'lucide-react';
import { ChevronDown, Code2, MoreHorizontal, X } from 'lucide-react';
import React, { useEffect, useState } from 'react';

import { Code, CodeDisplay, IconButton } from '@/components';
import { Button, Code, CodeDisplay } from '@/components';
import { tw } from '@/utils';

enum TokenType {
Expand Down Expand Up @@ -107,32 +107,35 @@ export default function Authorization({ value }: AuthorizationProps) {
{tokenState !== TokenState.Minimal && (
<div className={tw('flex flex-row gap-2 bg-gray-200 px-4 pt-4')}>
<>
<IconButton
<Button
data-test-id="token-expanded-button"
Icon={MoreHorizontal}
title="View full token"
className={tw(tokenState === TokenState.Expanded && 'bg-neutral')}
disabled={tokenState === TokenState.Expanded}
size="small"
selected={tokenState === TokenState.Expanded}
onClick={() => setTokenState(TokenState.Expanded)}
>
Full token
</IconButton>
</Button>
{decodedToken && (
<IconButton
<Button
data-test-id="token-decoded-button"
Icon={Code2}
title="Decode token"
className={tw(tokenState === TokenState.Decoded && 'bg-neutral')}
size="small"
selected={tokenState === TokenState.Decoded}
onClick={() => setTokenState(TokenState.Decoded)}
>
Decoded token
</IconButton>
</Button>
)}
<IconButton
<Button
data-test-id="token-minimal-button"
Icon={ChevronUp}
Icon={X}
title="Collapse"
className="ml-auto px-0"
className="ml-auto"
border="none"
size="small"
onClick={() => setTokenState(TokenState.Minimal)}
/>
</>
Expand Down
23 changes: 20 additions & 3 deletions packages/webui/src/components/Button.stories.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Meta, StoryObj } from '@storybook/react';
import { Trash } from 'lucide-react';

import Button from './Button';

Expand All @@ -9,12 +10,15 @@ const meta = {
layout: 'centered',
},
argTypes: {
selected: {
control: { type: 'boolean' },
},
size: {
options: ['small', 'standard', 'large'],
options: ['small', 'standard'],
control: { type: 'select' },
},
border: {
options: ['standard', 'ghost'],
options: ['standard', 'none'],
control: { type: 'select' },
},
},
Expand All @@ -23,10 +27,23 @@ const meta = {
export default meta;
type Story = StoryObj<typeof meta>;

export const Standard: Story = {
export const TextOnly: Story = {
args: {
children: 'Standard Button',
size: 'standard',
border: 'standard',
},
};

export const IconOnly: Story = {
args: {
Icon: Trash,
},
};

export const IconButton: Story = {
args: {
children: 'Trash Can',
Icon: Trash,
},
};
37 changes: 37 additions & 0 deletions packages/webui/src/components/Button.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import { createRef } from 'react';
import Button from './Button';

describe('Button', () => {
function Icon() {
return <div data-test-id="mock-icon">Mock Icon component</div>;
}

afterEach(() => {
cleanup();
jest.resetAllMocks();
Expand Down Expand Up @@ -37,4 +41,37 @@ describe('Button', () => {
const button = screen.getByRole('button');
expect(button).toBe(ref.current);
});

it('should render icon before button label', () => {
const { getByTestId } = render(<Button Icon={Icon}>Button label</Button>);

const icon = getByTestId('mock-icon');

expect(icon).toBeVisible();
expect(icon.nextSibling).toHaveTextContent('Button label');
});

it('should call onClick handler when clicked', async () => {
const handler = jest.fn();
render(
<Button role="button" onClick={handler}>
Button
</Button>,
);

expect(handler).not.toHaveBeenCalled();

const button = screen.getByRole('button');
await userEvent.click(button);

expect(handler).toHaveBeenCalled();
});

it('should forward ref to button', async () => {
const ref = createRef<HTMLButtonElement>();
render(<Button ref={ref}>Button</Button>);

const button = screen.getByRole('button');
expect(button).toBe(ref.current);
});
});
46 changes: 39 additions & 7 deletions packages/webui/src/components/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ import { MouseEvent, Ref, RefObject, forwardRef, useRef } from 'react';
import { tw } from '@/utils';

export type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {
size?: 'small' | 'standard' | 'large';
border?: 'standard' | 'ghost';
border?: 'standard' | 'none';
Icon?: React.FC<any>;
selected?: boolean;
size?: 'small' | 'standard';
};

function Button(
{ onClick, className, size = 'standard', border = 'standard', children, ...props }: ButtonProps,
{ Icon, selected, onClick, className, size = 'standard', border = 'standard', children, ...props }: ButtonProps,
ref: Ref<HTMLButtonElement>,
) {
const buttonRef = useRef<HTMLButtonElement>(null);
Expand All @@ -23,15 +25,45 @@ function Button(
<button
ref={finalRef}
className={tw(
'p-2 text-secondary bg-primary border border-solid border-primary rounded-md shadow-sm hover:bg-gray-50',
border === 'ghost' && 'bg-transparent border-transparent shadow-none',
size === 'small' && 'text-sm h-7 p-1',
size === 'large' && 'h-10',
// layout
'inline-flex items-center gap-x-1.5 p-1.5',
!!children && 'px-2',

// common styles
'border border-solid rounded-[0.25rem] font-bold shadow-sm uppercase',
border === 'none' && 'border-none shadow-none',

// base colors
'text-manatee-900 border-manatee-600 bg-manatee-100',
border === 'none' && 'bg-transparent',

// disabled
'disabled:text-manatee-400 disabled:border-manatee-300 disabled:bg-manatee-100',
border === 'none' && 'bg-transparent',

// focus
'focus:text-apple-900 focus:border-apple-600 focus:bg-apple-200',

// hover
'hover:text-apple-900 hover:border-apple-700 hover:bg-apple-200',

// pressed
'active:text-apple-950 active:border-apple-500 active:bg-apple-500',

// selected
selected && 'text-apple-950 border-apple-400 bg-apple-400',

// small
size === 'small' && 'p-1 text-xs',
size === 'small' && !!children && 'px-2',

// externals
className,
)}
onClick={handleClick}
{...props}
>
{Icon && <Icon size={size === 'small' ? 12 : 24} />}
{children}
</button>
);
Expand Down
4 changes: 2 additions & 2 deletions packages/webui/src/components/DarkModeToggle.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { MoonStar, SunMedium } from 'lucide-react';
import { useState } from 'react';

import IconButton from './IconButton';
import Button from './Button';

export default function DarkModeToggle() {
const initialTheme = localStorage.theme === 'dark';
Expand All @@ -23,5 +23,5 @@ export default function DarkModeToggle() {

const Icon = useDarkMode ? MoonStar : SunMedium;

return <IconButton Icon={Icon} onClick={handleCheckboxChange} role="toggle" />;
return <Button Icon={Icon} onClick={handleCheckboxChange} role="toggle" />;
}
24 changes: 0 additions & 24 deletions packages/webui/src/components/IconButton.stories.ts

This file was deleted.

26 changes: 0 additions & 26 deletions packages/webui/src/components/IconButton.test.tsx

This file was deleted.

22 changes: 0 additions & 22 deletions packages/webui/src/components/IconButton.tsx

This file was deleted.

63 changes: 29 additions & 34 deletions packages/webui/src/components/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,40 +61,35 @@ function Input({ className, onChange, Icon, focusKey, type, ...props }: InputPro
}

return (
<div className={className}>
<label htmlFor="search" className="sr-only">
Search
</label>
<div className="relative">
{Icon && (
<div className="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
<Icon className="h-5 w-5 text-neutral" aria-hidden="true" />
</div>
)}
<input
className="block w-full rounded-md border-0 bg-neutral py-2 pl-10 pr-3 ring-1 ring-primary placeholder:text-gray-500 focus:ring-lime-600"
ref={finalRef}
type={inputType}
onChange={handleChange}
value={value}
{...props}
/>
{!value && focusKey && specialKey && (
<span data-test-id="focus-key" className="absolute flex items-center inset-y-0 right-0 pr-3 text-neutral">
{specialKey}
{focusKey}
</span>
)}
{!!value && (
<span
data-test-id="input-clear"
className="absolute flex items-center inset-y-0 right-0 pr-3 text-neutral cursor-pointer"
onClick={clearValue}
>
<X />
</span>
)}
</div>
<div className="relative">
{Icon && (
<div className="pointer-events-none absolute inset-y-0 left-0 flex items-center ml-2">
<Icon size={24} className="text-manatee-600" aria-hidden="true" />
</div>
)}
<input
className="flex w-full pl-10 pr-3 py-1.5 rounded-xl bg-white text-manatee-600 border border-solid border-manatee-600 placeholder:text-manatee-600 focus:border-manatee-700 focus:text-manatee-700"
ref={finalRef}
type={inputType}
onChange={handleChange}
value={value}
{...props}
/>
{!value && focusKey && specialKey && (
<span data-test-id="focus-key" className="absolute flex items-center inset-y-0 right-0 pr-3 text-neutral">
{specialKey}
{focusKey}
</span>
)}
{!!value && (
<span
data-test-id="input-clear"
className="absolute flex items-center inset-y-0 right-0 pr-3 text-neutral cursor-pointer"
onClick={clearValue}
>
<X />
</span>
)}
</div>
);
}
Expand Down
Loading

0 comments on commit 36b5e70

Please sign in to comment.