Skip to content

Commit

Permalink
Merge pull request #247 from US-CBP/feature/file-input
Browse files Browse the repository at this point in the history
Feature/file input
  • Loading branch information
dgibson666 authored Feb 12, 2025
2 parents 0349ffa + e913973 commit 80fdcb3
Show file tree
Hide file tree
Showing 16 changed files with 590 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export const CbpDrawer = /*@__PURE__*/createReactComponent<JSX.CbpDrawer, HTMLCb
export const CbpDropdown = /*@__PURE__*/createReactComponent<JSX.CbpDropdown, HTMLCbpDropdownElement>('cbp-dropdown');
export const CbpDropdownItem = /*@__PURE__*/createReactComponent<JSX.CbpDropdownItem, HTMLCbpDropdownItemElement>('cbp-dropdown-item');
export const CbpExpand = /*@__PURE__*/createReactComponent<JSX.CbpExpand, HTMLCbpExpandElement>('cbp-expand');
export const CbpFileInput = /*@__PURE__*/createReactComponent<JSX.CbpFileInput, HTMLCbpFileInputElement>('cbp-file-input');
export const CbpFlex = /*@__PURE__*/createReactComponent<JSX.CbpFlex, HTMLCbpFlexElement>('cbp-flex');
export const CbpFlexItem = /*@__PURE__*/createReactComponent<JSX.CbpFlexItem, HTMLCbpFlexItemElement>('cbp-flex-item');
export const CbpFooter = /*@__PURE__*/createReactComponent<JSX.CbpFooter, HTMLCbpFooterElement>('cbp-footer');
Expand All @@ -49,6 +50,8 @@ export const CbpSegmentedButtonGroup = /*@__PURE__*/createReactComponent<JSX.Cbp
export const CbpSkipNav = /*@__PURE__*/createReactComponent<JSX.CbpSkipNav, HTMLCbpSkipNavElement>('cbp-skip-nav');
export const CbpStructuredList = /*@__PURE__*/createReactComponent<JSX.CbpStructuredList, HTMLCbpStructuredListElement>('cbp-structured-list');
export const CbpStructuredListItem = /*@__PURE__*/createReactComponent<JSX.CbpStructuredListItem, HTMLCbpStructuredListItemElement>('cbp-structured-list-item');
export const CbpSubnav = /*@__PURE__*/createReactComponent<JSX.CbpSubnav, HTMLCbpSubnavElement>('cbp-subnav');
export const CbpSubnavItem = /*@__PURE__*/createReactComponent<JSX.CbpSubnavItem, HTMLCbpSubnavItemElement>('cbp-subnav-item');
export const CbpTab = /*@__PURE__*/createReactComponent<JSX.CbpTab, HTMLCbpTabElement>('cbp-tab');
export const CbpTabPanel = /*@__PURE__*/createReactComponent<JSX.CbpTabPanel, HTMLCbpTabPanelElement>('cbp-tab-panel');
export const CbpTable = /*@__PURE__*/createReactComponent<JSX.CbpTable, HTMLCbpTableElement>('cbp-table');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ const Template = ({ label, tag, type, value, href, rel, target, download, fill,
${href ? `href=${href}` : ''}
${rel ? `rel=${rel}` : ''}
${target ? `target=${target}` : ''}
${download ? `download=${download}` : ''}
${download ? 'download' : ''}
${fill ? `fill=${fill}` : ''}
${color ? `color=${color}` : ''}
${variant !== 'default' ? `variant=${variant}` : ''}
Expand All @@ -123,7 +123,7 @@ const Template = ({ label, tag, type, value, href, rel, target, download, fill,
${targetProp ? `target-prop=${targetProp}` : ''}
${pressed ? `pressed=${pressed}` : ''}
${expanded ? `expanded=${expanded}` : ''}
${disabled ? `disabled=${disabled}` : ''}
${disabled ? 'disabled' : ''}
${context && context != 'light-inverts' ? `context=${context}` : ''}
${sx ? `sx=${JSON.stringify(sx)}` : ''}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export class CbpButton {
//@Prop() pointerOnly: boolean;

/** Marks the rendered button/link in a disabled state when specified. */
@Prop() disabled: boolean;
@Prop({reflect: true}) disabled: boolean;

/** Specifies the context of the component as it applies to the visual design and whether it inverts when light/dark mode is toggled. Default behavior is "light-inverts" and does not have to be specified. */
@Prop({ reflect: true }) context: 'light-inverts' | 'light-always' | 'dark-inverts' | 'dark-always';
Expand Down Expand Up @@ -191,6 +191,7 @@ export class CbpButton {
};

if (this.host.querySelector('[slot=cbp-button-custom]')) {
if (this.disabled) this.button.setAttribute("disabled",'');
return (
<Host onClick={(e) => this.handleClick(e)}>
<slot name="cbp-button-custom" />
Expand All @@ -203,12 +204,11 @@ export class CbpButton {
<button
{...this.persistedAttrs}
{...attrs}
//tabindex={this.pointerOnly || this.disabled ? -1 : 0}
disabled={this.disabled}
aria-label={this.accessibilityText}
aria-pressed={pressed ? 'true' : null}
aria-expanded={expanded ? 'true' : null}
aria-controls={this.controls}
//onClick={() => this.handleClick()}
ref={el => (this.button = el)}
>
<slot />
Expand All @@ -222,14 +222,12 @@ export class CbpButton {
<a
{...this.persistedAttrs}
{...attrs}
//tabindex={this.pointerOnly || this.disabled ? -1 : 0}
aria-label={this.accessibilityText}
aria-pressed={pressed ? 'true' : null}
aria-expanded={expanded ? 'true' : null}
aria-controls={this.controls}
role={disabled ? 'link' : null}
aria-disabled={disabled ? 'true' : null}
//onClick={() => this.handleClick()}
ref={el => (this.button = el)}
>
<slot />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,18 +117,20 @@ cbp-checkbox {
}

input[type=checkbox] {
appearance: none; // checkboxes do not accept styling per design specs
display: grid;
place-content: center;
appearance: none; // checkboxes do not accept styling per design specs
flex-grow: 0;
color: var(--cbp-checkbox-color);
background-color: var(--cbp-checkbox-color-bg);
min-height: unset; // unset from cbp-form-field
height: var(--cbp-space-6x);
width: var(--cbp-space-6x);
margin: 0;
border-color: var(--cbp-checkbox-color-border);
border-style: solid;
border-width: var(--cbp-border-size-md);
border-radius: var(--cbp-border-radius-soft);
height: var(--cbp-space-6x);
width: var(--cbp-space-6x);
margin: 0;
outline: 0;
box-shadow: 0 0 0 calc(var(--cbp-space-5x) / 2) var(--cbp-checkbox-color-halo);
clip-path: circle(86%);
Expand All @@ -137,7 +139,6 @@ cbp-checkbox {
&::before {
content: '';
overflow: hidden;
//transform: scale(0);
}

// Verified: only need to set the base variables with higher level tokens that are swapped for dark mode
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ const SingleSelectDropdownTemplate = ({ label, description, fieldId, name, place
${sx ? `sx=${JSON.stringify(sx)}` : ''}
>
${generateItems(items)}
</ul>
</cbp-dropdown>
</cbp-form-field>
`;
Expand Down Expand Up @@ -164,7 +163,6 @@ const StatesDropdownTemplate = ({ label, description, fieldId, name, placeholder
${sx ? `sx=${JSON.stringify(sx)}` : ''}
>
${generateItems(items)}
</ul>
</cbp-dropdown>
</cbp-form-field>
`;
Expand Down Expand Up @@ -244,7 +242,6 @@ const CountriesDropdownTemplate = ({ label, description, fieldId, name, placehol
${sx ? `sx=${JSON.stringify(sx)}` : ''}
>
${generateItems(items)}
</ul>
</cbp-dropdown>
</cbp-form-field>
`;
Expand Down Expand Up @@ -301,7 +298,6 @@ const MultiSelectDropdownTemplate = ({ label, description, fieldId, name, filter
${sx ? `sx=${JSON.stringify(sx)}` : ''}
>
${generateMultiSelectItems(items, context)}
</ul>
</cbp-dropdown>
</cbp-form-field>
`;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { Meta, Markdown } from "@storybook/blocks";
import Docs from './readme.md?raw';

<Meta title="Components/File Input/API Docs" />

<Markdown>{Docs}</Markdown>
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
/**
* @prop --cbp-file-input-color: var(--cbp-color-text-darkest);
* @prop --cbp-file-input-color-bg: var(--cbp-color-white);
* @prop --cbp-file-input-color-border: var(--cbp-color-interactive-secondary-base);
* @prop --cbp-file-input-color-border-hover: var(--cbp-color-interactive-secondary-darker);
* @prop --cbp-file-input-color-border-focus: var(--cbp-color-interactive-focus-dark);
* @prop --cbp-file-input-color-dark: var(--cbp-color-text-lightest);
* @prop --cbp-file-input-color-bg-dark: var(--cbp-color-gray-cool-70);
* @prop --cbp-file-input-color-border-dark: var(--cbp-color-interactive-secondary-light);
* @prop --cbp-file-input-color-border-hover-dark: var(--cbp-color-interactive-secondary-lighter);
* @prop --cbp-file-input-color-border-focus-dark:var(--cbp-color-interactive-focus-light);
* @prop --cbp-file-input-file-color-bg: var(--cbp-color-gray-cool-10);
* @prop --cbp-file-input-file-color-bg-dark: var(--cbp-color-gray-cool-60);
*/

:root{
--cbp-file-input-color: var(--cbp-color-text-darkest);
--cbp-file-input-color-bg: var(--cbp-color-white);
--cbp-file-input-color-border: var(--cbp-color-interactive-secondary-base);
--cbp-file-input-color-border-hover: var(--cbp-color-interactive-secondary-darker);
--cbp-file-input-color-border-focus: var(--cbp-color-interactive-focus-dark);

--cbp-file-input-color-dark: var(--cbp-color-text-lightest);
--cbp-file-input-color-bg-dark: var(--cbp-color-gray-cool-70);
--cbp-file-input-color-border-dark: var(--cbp-color-interactive-secondary-light);
--cbp-file-input-color-border-hover-dark: var(--cbp-color-interactive-secondary-lighter);
--cbp-file-input-color-border-focus-dark:var(--cbp-color-interactive-focus-light);

--cbp-file-input-file-color-bg: var(--cbp-color-gray-cool-10);
--cbp-file-input-file-color-bg-dark: var(--cbp-color-gray-cool-60);
}

// Displays dark design based on mode or context
[data-cbp-theme=light] cbp-file-input[context*=dark],
[data-cbp-theme=dark] cbp-file-input:not([context=dark-inverts]):not([context=light-always]) {
--cbp-file-input-color-label: var(--cbp-file-input-color-label-dark);
--cbp-file-input-color: var(--cbp-file-input-color-dark);
--cbp-file-input-color-bg: var(--cbp-file-input-color-bg-dark);
--cbp-file-input-color-border: var(--cbp-file-input-color-border-dark);
--cbp-file-input-color-border-hover: var(--cbp-file-input-color-border-hover-dark);
--cbp-file-input-color-border-focus: var(--cbp-file-input-color-border-focus-dark);
--cbp-file-input-file-color-bg: var(--cbp-file-input-file-color-bg-dark);
}


cbp-file-input {
display: block;
max-width: 22rem;
container-type: inline-size;

// Disabled field results in disabled styles applied at a higher level
&:has(input:disabled) {
--cbp-file-input-color: var(--cbp-color-interactive-disabled-dark);
--cbp-file-input-color-dark: var(--cbp-color-interactive-disabled-light);
--cbp-file-input-color-bg: transparent;
--cbp-file-input-color-bg-dark: transparent;
--cbp-file-input-color-border: var(--cbp-color-interactive-disabled-dark);
--cbp-file-input-color-border-dark: var(--cbp-color-interactive-disabled-light);
--cbp-file-input-file-color-bg: var(--cbp-color-interactive-disabled-light);
--cbp-file-input-file-color-bg-dark: var(--cbp-color-interactive-disabled-dark);
}

.cbp-file-input-wrapper {
display: flex;
align-items: center;
position: relative;
width: 100%;
max-width: 100%;
min-height: 4.5rem;
margin-block-end: var(--cbp-space-4x);

color: var(--cbp-file-input-color);
background-color: var(--cbp-file-input-color-bg);
border-width: var(--cbp-border-size-md);
border-color: var(--cbp-file-input-color-border);
border-radius: var(--cbp-file-input-border-radius);
outline-style: solid;
outline-width: 0;
outline-color: var(--cbp-file-input-color-border-focus);
outline-offset: -1px; // Verified: Safari does not show focus outline without this

border-style: dashed;
outline-offset: calc(var(--cbp-space-1x) * -1);


// Interactive are not applied to disabled fields
&:hover:not(:has(input:disabled)) {
--cbp-file-input-color-border: var(--cbp-file-input-color-border-hover);

cbp-button[fill=solid][color=secondary] {
--cbp-button-color: var(--cbp-button-color-hover);
--cbp-button-color-bg: var(--cbp-button-color-bg-hover);
--cbp-button-color-border: var(--cbp-button-color-border-hover);

--cbp-button-color-dark: var(--cbp-button-color-hover-dark);
--cbp-button-color-bg-dark: var(--cbp-button-color-bg-hover-dark);
--cbp-button-color-border-dark: var(--cbp-button-color-border-hover-dark);
}
}

// Interactive are not applied to disabled fields
&:focus:not(:has(input:disabled)),
&:focus-within:not(:has(input:disabled)) {
--cbp-file-input-color-border: var(--cbp-file-input-color-border-focus);
outline-width: var(--cbp-border-size-lg); // This is set to 3px because of the 1px inset, overlapping the border; border+outline = 4px total.

cbp-button[fill=solid][color=secondary] {
--cbp-button-color: var(--cbp-button-color-focus);
--cbp-button-color-bg: var(--cbp-button-color-bg-focus);
--cbp-button-color-border: var(--cbp-button-color-border-focus);

--cbp-button-color-dark: var(--cbp-button-color-focus-dark);
--cbp-button-color-bg-dark: var(--cbp-button-color-bg-focus-dark);
--cbp-button-color-border-dark: var(--cbp-button-color-border-focus-dark);

button {
outline-width: var(--cbp-border-size-md);
}
}
}

&[readonly],
&:disabled {
font-style: italic;
cursor: not-allowed;
}

// The visual layout of the field that lies underneath the invisible native input
.cbp-file-input-visuals {
display: flex;
align-items: center;
gap: var(--cbp-space-3x);
height: 100%;
padding-inline-start: var(--cbp-space-4x);
padding-inline-end: var(--cbp-space-3x);

.cbp-file-input-text {
display: flex;
align-items: center;
font-style: italic;
text-wrap: pretty;
}
}

input[type=file]:not(#fakeId) {
appearance: none;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
opacity: 0;
z-index: 2;
}
}


// The list of files beneath the input
.cbp-file-input-filelist {
display: flex;
flex-direction: column;
gap: var(--cbp-space-4x);

div {
display: flex;
align-items: center;
gap: var(--cbp-space-2x);
padding: var(--cbp-space-2x);
background-color: var(--cbp-file-input-file-color-bg);
font-weight: var(--cbp-font-weight-medium);
font-style: italic;
line-height: var(--cbp-line-height-xs);
overflow: hidden;
text-overflow: ellipsis;

:first-child {
flex-grow: 9;
}

cbp-button:not(#fakeId) {
--cbp-button-padding: 0;
--cbp-button-min-width: var(--cbp-space-7x);
--cbp-button-min-height: var(--cbp-space-7x);
--cbp-button-width: var(--cbp-space-7x);
--cbp-button-height: var(--cbp-space-7x);
--cbp-button-border-radius: var(--cbp-border-radius-circle);
--cbp-button-color-hover: var(--cbp-color-text-lightest);
--cbp-button-color-hover-dark: var(--cbp-color-text-darkest);
--cbp-button-color-bg-hover: var(--cbp-color-interactive-secondary-darker);
--cbp-button-color-bg-hover-dark: var(--cbp-color-interactive-secondary-lighter);
--cbp-button-color-border-hover: var(--cbp-color-interactive-secondary-darker);
--cbp-button-color-border-hover-dark: var(--cbp-color-interactive-secondary-lighter);
}
}
}

}


@container (width <= 19rem) {
.cbp-file-input-visuals cbp-icon {
display: none;
}
}
Loading

0 comments on commit 80fdcb3

Please sign in to comment.