Skip to content
Merged
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
5 changes: 5 additions & 0 deletions .changeset/tiny-clubs-behave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@ithaka/pharos': minor
---

Add full-width attribute to radio and checkbox components, which makes them fill their parent container. Default radio-group and checkbox-group width to 100%
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
.checkbox-group {
border: 0;
padding: 0;
width: 100%;
}

.checkbox-group__checkboxes {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { action } from 'storybook/actions';

import { PharosCheckbox, PharosLink } from '../../react-components';
import { PharosCheckbox, PharosCheckboxGroup, PharosLink } from '../../react-components';
import { defaultArgs, type ComponentArgs, type StoryArgs } from './storyArgs';
import { configureDocsPage } from '../../utils/_storybook/docsPageConfig';
import { PharosContext } from '../../utils/PharosContext';
Expand Down Expand Up @@ -122,6 +122,65 @@ export const Validity: Story = {
},
};

export const FullWidth: Story = {
render: () => (
<>
<style>{`
.full-width-styled-example [data-pharos-component='PharosCheckbox'] {
box-sizing: border-box;
padding: var(--pharos-spacing-one-half-x, 0.5rem);
border: 1px solid var(--pharos-color-marble-gray-80, #c3c5c8);
border-radius: var(--pharos-radius-base, 4px);
}
/* When a checkbox is checked, style it like an info alert */
.full-width-styled-example [data-pharos-component='PharosCheckbox'][checked] {
background-color: var(--pharos-alert-color-background-info);
border-color: var(--pharos-alert-color-border-info);
}
`}</style>
<div style={{ display: 'flex', flexDirection: 'column', gap: '3rem' }}>
<PharosCheckboxGroup
style={{
width: '480px',
border: '1px solid var(--pharos-color-marble-gray-80, #c3c5c8)',
padding: 'var(--pharos-spacing-one, 1rem)',
borderRadius: 'var(--pharos-radius-base, 4px)',
}}
>
<span slot="legend">Full Width</span>
<PharosCheckbox value="email" fullWidth checked>
<span slot="label">This is the first choice</span>
</PharosCheckbox>
<PharosCheckbox value="product" fullWidth>
<span slot="label">This is the second choice</span>
</PharosCheckbox>
<PharosCheckbox value="research" fullWidth>
<span slot="label">
This is the third choice with a label that is just entirely too long and someone
should have said something before shipping this to users
</span>
</PharosCheckbox>
</PharosCheckboxGroup>
<PharosCheckboxGroup className="full-width-styled-example" style={{ width: '480px' }}>
<span slot="legend">Full Width with styles</span>
<PharosCheckbox value="email" fullWidth checked>
<span slot="label">This is the first choice</span>
</PharosCheckbox>
<PharosCheckbox value="product" fullWidth>
<span slot="label">This is the second choice</span>
</PharosCheckbox>
<PharosCheckbox value="research" fullWidth>
<span slot="label">
This is the third choice with a label that is just entirely too long and someone
should have said something before shipping this to users

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

😂

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Gotta demo those real life use cases 😄

</span>
</PharosCheckbox>
</PharosCheckboxGroup>
</div>
</>
),
};

export const IsOnBackground: Story = {
name: 'On background',
render: () => (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,15 @@
@include mixins.option-wrapper;
}

:host([full-width]) {
width: 100%;
}

// Grow the label to fill the row so the entire width is clickable, not just the text
:host([full-width]) label {
flex: 1;
}

.input__icon {
display: block;
cursor: pointer;
Expand Down
22 changes: 22 additions & 0 deletions packages/pharos/src/components/checkbox/pharos-checkbox.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,28 @@ describe('pharos-checkbox', () => {
expect(clickSpy.callCount).to.equal(1);
});

it('stretches to fill its container when full-width is set', async () => {
const parentNode = document.createElement('div');
parentNode.style.width = '400px';
component = await fixture(
html`<test-pharos-checkbox full-width
><span slot="label">test checkbox</span></test-pharos-checkbox
>`,
{ parentNode }
);
expect(getComputedStyle(component).width).to.equal('400px');
});

it('does not stretch to fill its container by default', async () => {
const parentNode = document.createElement('div');
parentNode.style.width = '400px';
component = await fixture(
html`<test-pharos-checkbox><span slot="label">test checkbox</span></test-pharos-checkbox>`,
{ parentNode }
);
expect(getComputedStyle(component).width).to.not.equal('400px');
});

it('resets checked when the form is reset', async () => {
const parentNode = document.createElement('form');
parentNode.setAttribute('name', 'my-form');
Expand Down
7 changes: 7 additions & 0 deletions packages/pharos/src/components/checkbox/pharos-checkbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ export class PharosCheckbox extends FormMixin(FormElement) {
@property({ type: Boolean, reflect: true })
public isOnBackground = false;

/**
* Stretches the checkbox to fill the width of its container
* @attr full-width
*/
@property({ type: Boolean, reflect: true })
public fullWidth = false;

@query('#checkbox-element')
private _checkbox!: HTMLInputElement;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,57 @@ export const Validity: Story = {
},
};

export const FullWidth: Story = {
render: () =>
html` <style>
.full-width-styled-example storybook-pharos-checkbox {
box-sizing: border-box;
padding: var(--pharos-spacing-one-half-x, 0.5rem);
border: 1px solid var(--pharos-color-marble-gray-80, #c3c5c8);
border-radius: var(--pharos-radius-base, 4px);
}
/* When a checkbox is checked, style it like an info alert */
.full-width-styled-example storybook-pharos-checkbox[checked] {
background-color: var(--pharos-alert-color-background-info);
border-color: var(--pharos-alert-color-border-info);
}
</style>
<div style="display: flex; flex-direction: column; gap: 3rem;">
<storybook-pharos-checkbox-group
style="width: 480px; border: 1px solid var(--pharos-color-marble-gray-80, #c3c5c8); padding: var(--pharos-spacing-one, 1rem); border-radius: var(--pharos-radius-base, 4px);"
>
<span slot="legend">Full Width</span>
<storybook-pharos-checkbox value="email" full-width checked
><span slot="label">This is the first choice</span></storybook-pharos-checkbox
>
<storybook-pharos-checkbox value="product" full-width
><span slot="label">This is the second choice</span></storybook-pharos-checkbox
>
<storybook-pharos-checkbox value="research" full-width
><span slot="label"
>This is the third choice with a label that is just entirely too long and someone
should have said something before shipping this to users</span
></storybook-pharos-checkbox
>
</storybook-pharos-checkbox-group>
<storybook-pharos-checkbox-group class="full-width-styled-example" style="width: 480px;">
<span slot="legend">Full Width with styles</span>
<storybook-pharos-checkbox value="email" full-width checked
><span slot="label">This is the first choice</span></storybook-pharos-checkbox
>
<storybook-pharos-checkbox value="product" full-width
><span slot="label">This is the second choice</span></storybook-pharos-checkbox
>
<storybook-pharos-checkbox value="research" full-width
><span slot="label"
>This is the third choice with a label that is just entirely too long and someone
should have said something before shipping this to users</span
></storybook-pharos-checkbox
>
</storybook-pharos-checkbox-group>
</div>`,
};

export const IsOnBackground: Story = {
name: 'On background',
render: () => html`
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Fragment } from 'react';
import { action } from 'storybook/actions';

import { PharosRadioButton, PharosLink } from '../../react-components';
import { PharosRadioButton, PharosRadioGroup, PharosLink } from '../../react-components';
import { configureDocsPage } from '../../utils/_storybook/docsPageConfig';
import { defaultArgs, type ComponentArgs, type StoryArgs } from './storyArgs';
import { PharosContext } from '../../utils/PharosContext';
Expand Down Expand Up @@ -123,3 +123,67 @@ export const Validity: Story = {
message: 'This field is required, please make a selection',
},
};

export const FullWidth: Story = {
render: () => (
<>
<style>{`
.full-width-styled-example [data-pharos-component='PharosRadioButton'] {
box-sizing: border-box;
padding: var(--pharos-spacing-one-half-x, 0.5rem);
border: 1px solid var(--pharos-color-marble-gray-80, #c3c5c8);
border-radius: var(--pharos-radius-base, 4px);
}
/* When a radio button is checked, style it like an info alert */
.full-width-styled-example [data-pharos-component='PharosRadioButton'][checked] {
background-color: var(--pharos-alert-color-background-info);
border-color: var(--pharos-alert-color-border-info);
}
`}</style>
<div style={{ display: 'flex', flexDirection: 'column', gap: '3rem' }}>
<PharosRadioGroup
name="full-width-example"
style={{
width: '480px',
border: '1px solid var(--pharos-color-marble-gray-80, #c3c5c8)',
padding: 'var(--pharos-spacing-one, 1rem)',
borderRadius: 'var(--pharos-radius-base, 4px)',
}}
>
<span slot="legend">Full Width</span>
<PharosRadioButton value="first" fullWidth checked>
<span slot="label">This is the first choice</span>
</PharosRadioButton>
<PharosRadioButton value="second" fullWidth>
<span slot="label">This is the second choice</span>
</PharosRadioButton>
<PharosRadioButton value="third" fullWidth>
<span slot="label">
This is the third choice with a label that is just entirely too long and someone
should have said something before shipping this to users
</span>
</PharosRadioButton>
</PharosRadioGroup>
<PharosRadioGroup
name="full-width-styled-example"
className="full-width-styled-example"
style={{ width: '480px' }}
>
<span slot="legend">Full Width with styles</span>
<PharosRadioButton value="first" fullWidth checked>
<span slot="label">This is the first choice</span>
</PharosRadioButton>
<PharosRadioButton value="second" fullWidth>
<span slot="label">This is the second choice</span>
</PharosRadioButton>
<PharosRadioButton value="third" fullWidth>
<span slot="label">
This is the third choice with a label that is just entirely too long and someone
should have said something before shipping this to users
</span>
</PharosRadioButton>
</PharosRadioGroup>
</div>
</>
),
};
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,15 @@
@include mixins.option-wrapper;
}

:host([full-width]) {
width: 100%;
}

// Grow the label to fill the row so the entire width is clickable, not just the text
:host([full-width]) label {
flex: 1;
}

.input__icon {
display: block;
cursor: pointer;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,30 @@ describe('pharos-radio-button', () => {
expect(count).to.equal(1);
});

it('stretches to fill its container when full-width is set', async () => {
const parentNode = document.createElement('div');
parentNode.style.width = '400px';
component = await fixture(
html`<test-pharos-radio-button full-width
><span slot="label">test radio</span></test-pharos-radio-button
>`,
{ parentNode }
);
expect(getComputedStyle(component).width).to.equal('400px');
});

it('does not stretch to fill its container by default', async () => {
const parentNode = document.createElement('div');
parentNode.style.width = '400px';
component = await fixture(
html`<test-pharos-radio-button
><span slot="label">test radio</span></test-pharos-radio-button
>`,
{ parentNode }
);
expect(getComputedStyle(component).width).to.not.equal('400px');
});

it('resets checked when the form is reset', async () => {
const parentNode = document.createElement('form');
parentNode.setAttribute('name', 'my-form');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ export class PharosRadioButton extends FormMixin(FormElement) {
@property({ type: String, reflect: true })
public value = '';

/**
* Stretches the radio button to fill the width of its container
* @attr full-width
*/
@property({ type: Boolean, reflect: true })
public fullWidth = false;

@query('#radio-element')
private _radio!: HTMLInputElement;

Expand Down
Loading
Loading