Skip to content

Commit

Permalink
Merge pull request #218 from US-CBP/feature/cbp-radio
Browse files Browse the repository at this point in the history
Feature/cbp radio
  • Loading branch information
dgibson666 authored Oct 25, 2024
2 parents c5cf432 + 212cd3e commit d375f67
Show file tree
Hide file tree
Showing 20 changed files with 789 additions and 92 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export const CbpList = /*@__PURE__*/createReactComponent<JSX.CbpList, HTMLCbpLis
export const CbpNotice = /*@__PURE__*/createReactComponent<JSX.CbpNotice, HTMLCbpNoticeElement>('cbp-notice');
export const CbpPagination = /*@__PURE__*/createReactComponent<JSX.CbpPagination, HTMLCbpPaginationElement>('cbp-pagination');
export const CbpPanel = /*@__PURE__*/createReactComponent<JSX.CbpPanel, HTMLCbpPanelElement>('cbp-panel');
export const CbpRadio = /*@__PURE__*/createReactComponent<JSX.CbpRadio, HTMLCbpRadioElement>('cbp-radio');
export const CbpSection = /*@__PURE__*/createReactComponent<JSX.CbpSection, HTMLCbpSectionElement>('cbp-section');
export const CbpSegmentedButtonGroup = /*@__PURE__*/createReactComponent<JSX.CbpSegmentedButtonGroup, HTMLCbpSegmentedButtonGroupElement>('cbp-segmented-button-group');
export const CbpSkipNav = /*@__PURE__*/createReactComponent<JSX.CbpSkipNav, HTMLCbpSkipNavElement>('cbp-skip-nav');
Expand Down
4 changes: 0 additions & 4 deletions packages/web-components/assets/css/storybook-canvas.css
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,6 @@ body:has(cbp-app[data-cbp-theme="dark"]) {
}

/* TechDebt: Remove when table component is added to design tokens story */
#design-tokens h2 {
margin-block-end: 1rem;
}

#design-tokens table {
width: 100%;
border-collapse: collapse;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,5 @@ An Accordion is a common paradigm for progressive disclosure, organizing content

* If manually setting multiple Accordion Items to open via the property, the component will not force only one open even if `multiple` is not specified.
* So, you could specify all items open by default, regardless of this property.
* If `multiple` is not set to true, all Accordion Items will be closed when one is toggled to `open` via user interaction.
* If `multiple` is not set to true, all Accordion Items will be closed when one is toggled to `open` via user interaction.
* TODO: Investigate implementing `hidden="until-found"` for collapsed content. (https://developer.chrome.com/docs/css-ui/hidden-until-found)
4 changes: 1 addition & 3 deletions packages/web-components/src/components/cbp-app/cbp-app.scss
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
/* Temporary workaround for verified Stencil bug; can use @use due to compilation error */
//@import 'reset', 'roboto', 'css-variables', 'core';

cbp-app {
display: block;
position: relative;
width: 100%;
min-height: 100vh;
color: var(--cbp-color-body-text);
Expand Down
13 changes: 9 additions & 4 deletions packages/web-components/src/components/cbp-app/cbp-app.specs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,23 @@ import { Meta } from '@storybook/addon-docs';

## Purpose

The App component is a wrapper that packages high level design tokens, styles, and fonts as well as acting as top level control for things like dark mode within the design system/component library.
The App component is a wrapper that packages high level design tokens (as CSS variables), styles, and fonts as well as acting as top level control for things like dark mode within the design system/component library.

## Functional Requirements

* The App component contains necessary high-level CSS and fonts to remove the need for HTML tags referencing external dependencies.
* As the highest level container of the application, system-wide functionality may be hoisted to this tag, such as dark mode, performance monitoring, debugging, etc. (TBD)
* The App component contains necessary high-level CSS and fonts to remove the need for external dependencies referenced by HTML `link` tags.
* As the highest level container of the application, system-wide functionality may be hoisted to this tag, such as dark mode, debugging, etc. (TBD)

## Technical Specifications

### User Interactions

n/a
* By setting the `theme` property, the application may be set to light or dark mode independently of the user's operating system settings.
* By setting the `debug` property to true, debug info will be logged to the console, including:
* Application name, if specified
* Application version, if specified
* Design System version
* StencilJS version

### Responsiveness

Expand Down
19 changes: 17 additions & 2 deletions packages/web-components/src/components/cbp-app/cbp-app.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, Prop, Host, h } from '@stencil/core';
import { Component, Prop, Host, h, Env } from '@stencil/core';

/*
An overarching "app" tag can act as a low-barrier way to get core design system elements (CSS, fonts)
Expand All @@ -17,20 +17,35 @@ export class CbpApp {
/** Optionally specifies light/dark mode. This is only needed if the application can change the theme separate from OS settings. */
@Prop({reflect: true}) theme: "light" | "dark" | "system" = "system"

@Prop({reflect: true}) debug: boolean;

@Prop({reflect: true}) appName: string;
@Prop({reflect: true}) appVersion: string;

handleThemeChange(mql) {
this.theme = mql.matches ? "dark" : "light";
}

componentDidLoad() {
const darkMode = window?.matchMedia(`(prefers-color-scheme: dark)`);
// Only set up the listener if we're using the system default, otherwise, it's being set manually
// Only set up the listener if we're using the system default, otherwise it's being set manually via reactive property
if (this.theme == "system") {
darkMode.addEventListener('change', mql => this.handleThemeChange(mql)); // Add an event listener to the media query
this.handleThemeChange(darkMode); // Run the theme change handler once on load
}
}

render() {
// If debug is enabled, write debug info to the console
if (this.debug) {
let debugInfo = `DEBUGGING INFO:\n===============\n`;
if (this.appName) debugInfo += `Application name: ${this.appName}\n`;
if (this.appVersion) debugInfo += `Application version: ${this.appVersion}\n`;
debugInfo += `CBP Design System version: ${Env.version}\n`;
debugInfo += `Built with StencilJS: ${Env.stencil}`;
console.log(debugInfo);
}

return (
<Host data-cbp-theme={this.theme}>
<slot />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
cbp-checkbox {
display: block;
margin: var(--cbp-checkbox-margin);
position: relative;

label {
display: flex;
Expand All @@ -129,7 +130,7 @@ cbp-checkbox {
margin-inline-end: var(--cbp-space-2x);
outline: 0;
box-shadow: 0 0 0 calc(var(--cbp-space-5x) / 2) var(--cbp-checkbox-color-halo);
clip-path: circle(80%);
clip-path: circle(86%);

// Check Mark/Dash
&::before {
Expand Down Expand Up @@ -177,16 +178,18 @@ cbp-checkbox {
&::before {
border-right: solid var(--cbp-border-size-lg) var(--cbp-checkbox-color);
border-bottom: solid var(--cbp-border-size-lg) var(--cbp-checkbox-color);
//border-radius: 1px;
height: 70%;
width: 30%;
width: 35%;
transform: rotate(45deg) translateY(-10%) translateX(-10%);
}
}

&:indeterminate {
// Indeterminate dash
&::before {
border: solid var(--cbp-border-size-sm) var(--cbp-checkbox-color);
border: solid var(--cbp-border-size-md) var(--cbp-checkbox-color);
border-radius: var(--cbp-border-radius-soft);
height: 0;
width: 60%;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ The Checkbox component wraps the slotted native form control (`input type="check
* The Checkbox component accepts the native form control (`input type="checkbox"`) and its label as slotted content, wrapping them in an implicit label.
* The Checkbox component provides cross-browser styling for the form control in its various states, including hover, focus, disabled, and checked states.
* The Checkbox component allows the form control to be set to an indeterminate state for "select all" and grouping functionality.
* A checkbox may be used individually as a standalone checkbox or within a checklist.

## Technical Specifications

Expand All @@ -26,6 +27,7 @@ The Checkbox component wraps the slotted native form control (`input type="check
### Responsiveness

* The checkbox label will wrap as needed.
* The checkbox control is sized in relative units and will respond to changes in the user's default text size.

### Accessibility

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export class CbpCheckbox {
/** The `name` attribute of the checkbox, which is passed as part of formData (as a key) only when the checkbox is checked. */
@Prop() name: string;

/** The `value` attribute of the checkbox, which is passed as part of formData (as a value) only when the checkbox is checked. */
/** Optionally set the `value` attribute of the checkbox at the component level. Not needed if the slotted checkbox has a value. */
@Prop() value: string;

/** Marks the checkbox as checked by default when specified. */
Expand Down Expand Up @@ -64,6 +64,7 @@ export class CbpCheckbox {
@Watch('indeterminate')
watchIndeterminateHandler(newValue: boolean) {
if (this.formField) this.formField.indeterminate=newValue;
if (newValue == true) this.checked = false;
}

componentWillLoad() {
Expand All @@ -85,9 +86,11 @@ export class CbpCheckbox {
componentDidLoad() {
// Set the disabled/indeterminate states on load only if true. (The Watch decorators only listen for changes, not initial state)
if (!!this.formField) {
if (this.indeterminate) this.formField.indeterminate=this.indeterminate;
if (this.checked) this.formField.checked=this.checked;
if (this.indeterminate && !this.checked) this.formField.indeterminate=this.indeterminate; // Checked takes precedence
if (this.disabled) this.formField.setAttribute('disabled', '');
if (this.name) this.formField.name=this.name;
if (this.value) this.formField.value=this.value;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ cbp-form-field-wrapper {
flex-basis: 100%; // for child flex context

// Override the input padding based on overlay size to prevent input text from being obscured (text may still be obscured if there's not enough space for it)
input {
input:not(#fakeId) {
padding-inline-start: calc(var(--cbp-form-field-overlay-start-width) + var(--cbp-form-field-wrapper-padding-start));
padding-inline-end: calc(var(--cbp-form-field-overlay-end-width) + var(--cbp-form-field-wrapper-padding-end));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ cbp-form-field {
display: block;
margin-bottom: var(--cbp-form-field-margin-bottom);

fieldset,
legend {
all: unset;
}

.cbp-form-field-label {
display: block;
color: var(--cbp-form-field-color-label);
Expand All @@ -76,7 +81,8 @@ cbp-form-field {
position: relative;
}

input:not([type=checkbox]):not([type=radio]),
//input:not([type~="checkbox radio"]),
input:not([type="checkbox"]):not([type="radio"]),
textarea,
select,
.cbp-custom-form-control {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,14 @@ The Form Field component represents a generic, reusable pattern for form fields
## Functional Requirements

* The Form Field component enforces the structure of an input pattern, containing:
* A label
* An optional description (or errors)
* The native form control (input, select, textarea, etc.)
* A `label` tag.
* An optional description (or errors).
* The native form control (input, select, textarea, etc.) slotted.
* For compound input patterns containing multiple form controls (e.g., checklist, radio list, etc.), this includes:
* A wrapping `fieldset` tag.
* A `legend` tag representing the pattern label.
* An optional description (or errors).
* The native form controls (input, select, textarea, etc.) slotted, with their own labels.
* The Form Field component encapsulates styles for all types of HTML form fields that may be slotted within, including various states such as:
* readonly
* disabled
Expand All @@ -36,6 +41,13 @@ The Form Field component represents a generic, reusable pattern for form fields
* According to CBP Design System guidance, required fields should indicate "Required" in the field description in plain text.
* The `required` attribute should not be used on the native form field because 1. screen readers would read "required" twice and 2. this attribute triggers browser-based validation, which will not behave consistently with application/custom validation.
* Do not place `aria-required` on the native form field, as screen readers would read "required" twice.
* For single-input form fields:
* The label is rendered within a semantic `label` tag referencing the slotted form control by `id`.
* The (optional) description is associated to the form control as a description via the `aria-describedby` attribute.
* For multi-input form fields, such as radio lists or checklists:
* The entire patterns is wrapped within a `fieldset`, which provides an inherent "group" role.
* The component label is rendered as a `legend`, which provides the group's label.
* The (optional) description is associated to the `fieldset` via the `aria-describedby` attribute.
* Disabled form controls and buttons are non-interactive and cannot be navigated to using the keyboard and should be used with caution (if at all).
* Placeholder text should rarely, if ever, be used.
* Especially in forms where the user is expected to enter data, placeholder text with sufficient contrast to the background color may be mistaken as entered input.
Expand Down Expand Up @@ -69,4 +81,4 @@ The Form Field component represents a generic, reusable pattern for form fields
* Use of the `size` attribute is discouraged as it does not represent a linear/consistent scale across input sizes and browsers.
* Furthermore, `size` is not valid on some input types such as `type="number"`.
* When necessary, it is recommended to use CSS to style the width of form fields (using a relative unit such as `ch` or `rem`) separate from their containers.
* TODO: Handling of groups of inputs can be done via role=group or a legend tag. The legend tag can accept a disabled attribute, which is an advantage.
* TODO: needs additional testing of nested `cbp-form-field` components, making up compound input patterns (e.g., phone, address).
Loading

0 comments on commit d375f67

Please sign in to comment.