Skip to content

finished inputs #8

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

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
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
31 changes: 31 additions & 0 deletions .github/workflows/gh-pages-preview.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# .github/workflows/preview.yml
name: Deploy PR previews

on:
pull_request:
types:
- opened
- reopened
- synchronize
- closed

concurrency: preview-${{ github.ref }}

jobs:
deploy-preview:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Install and Build
if: github.event.action != 'closed' # You might want to skip the build if the PR has been closed
run: |
npm install
npm run build-storybook

- name: Deploy preview
uses: rossjrw/pr-preview-action@v1
with:
source-dir: ./storybook-static/
preview-branch: gh-pages
24 changes: 7 additions & 17 deletions .github/workflows/gh-pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ on:

# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
contents: write
pages: write
id-token: write

Expand All @@ -23,7 +23,7 @@ concurrency:

jobs:
# Build job
build:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
Expand All @@ -34,19 +34,9 @@ jobs:
run: |
npm install
npm run build-storybook
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
- uses: JamesIves/github-pages-deploy-action@v4
with:
path: storybook-static/

# Deployment job
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
folder: ./storybook-static/
branch: gh-pages
clean-exclude: pr-preview
force: false
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ node_modules
.cache
.next
storybook-static
dist
dist
package-lock.json
81 changes: 81 additions & 0 deletions src/components/dropdowns/dropdown.css
Copy link
Member

Choose a reason for hiding this comment

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

dropdown looks too tall

image

maybe missing box-sizing: border-box?

Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
.dropdown-wrapper {
font-family: sans-serif;
width: 291px;
user-select: none;
position: relative;
overflow: visible;
/* padding: 10px 20px 10px 20px; */

font-family: "DM Sans", sans-serif;
}

/* Button part (closed or open) */
.dropdown-header {
width: 100%;
padding: 10px 20px;
border-bottom: none;
font-size: 16px;
height: 67px;
border: 1px solid black;
border-radius: 16px;
display: flex;
justify-content: space-between;
align-items: center;
cursor: pointer;
background-color: white;
}

.dropdown-wrapper.open .dropdown-header {
border-radius: 16px 16px 0 0;
/* border-bottom-left-radius: 0;
border-bottom-right-radius: 0; */
}

/* Arrow */
.arrow {
width: 24px;
height: 24px;
flex-shrink: 0px;
}

/* Dropdown menu */
.dropdown-menu {
position: absolute;
top: 100%;
left: 0;
padding: 0px 20px 10px 20px;
width: 100%;
margin: 0;
display: flex;
list-style: none;
flex-direction: column;
gap: 10px;
background-color: white;
border: 1px solid black;
/* border-top: none; */
border-radius: 0 0 12px 12px;

z-index: 10;
}

/* Divider between header and options
.dropdown-menu::before {
content: '';
display: block;
height: 1px;
background-color: black;
margin-bottom: 8px;
} */

/* Each option */
.dropdown-item {
line-height: 24px;
font-size: 16px;
border-radius: 8px;
padding: 4px 8px;
cursor: pointer;
}

.dropdown-item:hover {
background-color: #f5f5f5;
}
49 changes: 49 additions & 0 deletions src/components/dropdowns/dropdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React, {useState} from 'react';
import './dropdown.css';

export interface DropdownProps {
theme?: 'light' | 'dark';
size?: 'desktop' | 'mobile';
suggestions?: string[];
onChange?: (value: string) => void;
}

export const Dropdown: React.FC<DropdownProps> = ({
theme = 'light',
size = "desktop",
suggestions = [],
onChange

}) => {
const [isOpen, setIsOpen] = useState(false);
const [selected, setIsSelected] = useState<string | null>(null);
// const options = {[...(selected ? [selected] : []), ...suggestions.filter(s => s !== selected)]};
// const filtered = selected ? suggestions.filter(s => s !== selected) : suggestions;
const options = [...suggestions].sort((a, b) => a.localeCompare(b));
const toggleDropdown = () => setIsOpen(!isOpen);
const handleSelect = (option: string) => {
setIsSelected(option);
setIsOpen(false);
onChange?.(option);
}

return (
<div className={`dropdown-wrapper ${theme} ${size}`}>
Copy link
Member

Choose a reason for hiding this comment

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

we should probably also have a <select> element so that you can tab focus on it, mostly for accessibility, but on keyboard you can also type the first few letters to select an option

<div className="dropdown-header" onClick={toggleDropdown}>
<span>{selected ?? 'Select an option'}</span>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="25" viewBox="0 0 24 25" fill="none">
<path d="M16.59 9.09L12 13.67L7.41 9.09L6 10.5L12 16.5L18 10.5L16.59 9.09Z" fill="black"/>
</svg>
</div>
{isOpen && (
<ul className="dropdown-menu">
{options.map((option, i) => (
<li key={i} className="dropdown-item" onClick={() => handleSelect(option)}>
{option}
</li>
))}
</ul>
)}
</div>
);
}
66 changes: 66 additions & 0 deletions src/components/inputs-dropdowns/Input.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import React, { useState, useRef } from 'react';
import './inputs.css'

export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
theme?: 'light' | 'dark'; // Define theme prop
variant?: 'primary' | 'error';
inputSize?: 'desktop' | 'mobile';
suggestions?: string[];
onValueChange?: (value: string) => void;

}

export const Input: React.FC<InputProps> = ({
theme = "light",
variant = 'primary',
inputSize = 'desktop',
suggestions = [],
onValueChange,
...rest
}) => {
const [value, setValue]= useState('');
const inputRef = useRef<HTMLInputElement>(null);

const bestSuggestion = suggestions.find(s =>
s.toLowerCase().startsWith(value.toLowerCase()) && s.toLowerCase() !== value.toLowerCase())
|| '';
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const newVal = e.target.value;
setValue(newVal);
onValueChange?.(newVal);
};
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if ((e.key === 'ArrowRight' || e.key === 'Tab') && bestSuggestion) {
e.preventDefault();
setValue(bestSuggestion);
onValueChange?.(bestSuggestion);

}
}
return (

<div className={`inline-suggest-wrapper ${inputSize}`}>
<input
ref={inputRef}
className={`input ${theme} ${variant} ${inputSize}`}
value={value}
onChange={handleChange}
onKeyDown={handleKeyDown}
{...rest}
/>
{value && bestSuggestion && (
<div className={`inline-suggestion ${theme}`}>
<span>{value}</span>
<span className="hint"
onClick={() => {
setValue(bestSuggestion);
onValueChange?.(bestSuggestion);
inputRef.current?.focus(); // optional: put focus back in input
}}
>{bestSuggestion.slice(value.length)}</span>
</div>
)}
</div>

);
}
94 changes: 94 additions & 0 deletions src/components/inputs-dropdowns/inputs.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@

.inline-suggest-wrapper {
position: relative;
display: inline-block;
width: 291px; /* match input width */
font-family: "DM Sans", sans-serif;
}

.inline-suggest-wrapper.desktop {
font-size: 1.125rem; /* match .input.desktop */
line-height: 1.5rem; /* match .input.desktop */
}
.inline-suggest-wrapper.mobile {
font-size: 1rem;
line-height: 1.3125rem;
}

.inline-suggestion {
top: 50%;
position: absolute;
transform: translateY(-50%);
pointer-events: none;
left: 0;
box-sizing: border-box;
/* padding: 10px 20px 10px 20px; */
padding: 0 20px;
height: 100%;
display: flex;
align-items: center;
font-family: inherit;
font-weight: 400;
font-size: inherit;
line-height: inherit;
white-space: pre;
}
.inline-suggestion .hint {
pointer-events: auto;
cursor: pointer;
}
.inline-suggestion span:first-child {
color: transparent;
user-select: none;
}
.inline-suggestion.light {
color: var(--Surface-Elevation-border, #979797);
}
.inline-suggestion.dark {
color: #797C8B;
}
.input {
position: relative;
width: 291px;
border-radius: 16px;
justify-content: space-between;
padding: 10px 20px 10px 20px;
font-family: "DM Sans", sans-serif;
font-weight: 400;
box-sizing: border-box;
letter-spacing: 0;
vertical-align: middle;
}


.input.light {
background-color: #FBFBFB;

}

.input.dark {
background-color: #2F3037;
color: var(--Surface-Text-color-text-color, #FFFFFF);

}

.input.primary {
border: 1px solid var(--Buttons-CTA-Button-Secondary-secondary-text, #000000);
}

.input.error {
border: 1px solid #E36370;
color: #E36370;
}

.input.desktop {
font-size: 1.125rem;
line-height: 1.5e;
height: 67px;
}

.input.mobile {
font-size: 1rem;
line-height: 1.3125rem;
height: 58px;
}
Loading