diff --git a/.changeset/tiny-clubs-behave.md b/.changeset/tiny-clubs-behave.md new file mode 100644 index 000000000..99a697a33 --- /dev/null +++ b/.changeset/tiny-clubs-behave.md @@ -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% diff --git a/packages/pharos/src/components/checkbox-group/pharos-checkbox-group.scss b/packages/pharos/src/components/checkbox-group/pharos-checkbox-group.scss index 1448572ba..9e3fe8fcc 100644 --- a/packages/pharos/src/components/checkbox-group/pharos-checkbox-group.scss +++ b/packages/pharos/src/components/checkbox-group/pharos-checkbox-group.scss @@ -6,6 +6,7 @@ .checkbox-group { border: 0; padding: 0; + width: 100%; } .checkbox-group__checkboxes { diff --git a/packages/pharos/src/components/checkbox/PharosCheckbox.react.stories.tsx b/packages/pharos/src/components/checkbox/PharosCheckbox.react.stories.tsx index 66bb8cca2..f8df2551d 100644 --- a/packages/pharos/src/components/checkbox/PharosCheckbox.react.stories.tsx +++ b/packages/pharos/src/components/checkbox/PharosCheckbox.react.stories.tsx @@ -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'; @@ -122,6 +122,65 @@ export const Validity: Story = { }, }; +export const FullWidth: Story = { + render: () => ( + <> + +
+ + Full Width + + This is the first choice + + + This is the second choice + + + + 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 + + + + + Full Width with styles + + This is the first choice + + + This is the second choice + + + + 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 + + + +
+ + ), +}; + export const IsOnBackground: Story = { name: 'On background', render: () => ( diff --git a/packages/pharos/src/components/checkbox/pharos-checkbox.scss b/packages/pharos/src/components/checkbox/pharos-checkbox.scss index d8bda8487..ddd9dd1ca 100644 --- a/packages/pharos/src/components/checkbox/pharos-checkbox.scss +++ b/packages/pharos/src/components/checkbox/pharos-checkbox.scss @@ -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; diff --git a/packages/pharos/src/components/checkbox/pharos-checkbox.test.ts b/packages/pharos/src/components/checkbox/pharos-checkbox.test.ts index 11d169984..fecadf7a6 100644 --- a/packages/pharos/src/components/checkbox/pharos-checkbox.test.ts +++ b/packages/pharos/src/components/checkbox/pharos-checkbox.test.ts @@ -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 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 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'); diff --git a/packages/pharos/src/components/checkbox/pharos-checkbox.ts b/packages/pharos/src/components/checkbox/pharos-checkbox.ts index 4f00e0673..b7672d055 100644 --- a/packages/pharos/src/components/checkbox/pharos-checkbox.ts +++ b/packages/pharos/src/components/checkbox/pharos-checkbox.ts @@ -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; diff --git a/packages/pharos/src/components/checkbox/pharos-checkbox.wc.stories.ts b/packages/pharos/src/components/checkbox/pharos-checkbox.wc.stories.ts index 62ecefbff..4699c1c11 100644 --- a/packages/pharos/src/components/checkbox/pharos-checkbox.wc.stories.ts +++ b/packages/pharos/src/components/checkbox/pharos-checkbox.wc.stories.ts @@ -109,6 +109,57 @@ export const Validity: Story = { }, }; +export const FullWidth: Story = { + render: () => + html` +
+ + Full Width + This is the first choice + This is the second choice + 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 + + + Full Width with styles + This is the first choice + This is the second choice + 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 + +
`, +}; + export const IsOnBackground: Story = { name: 'On background', render: () => html` diff --git a/packages/pharos/src/components/radio-button/PharosRadioButton.react.stories.tsx b/packages/pharos/src/components/radio-button/PharosRadioButton.react.stories.tsx index 0344dcc98..c92789121 100644 --- a/packages/pharos/src/components/radio-button/PharosRadioButton.react.stories.tsx +++ b/packages/pharos/src/components/radio-button/PharosRadioButton.react.stories.tsx @@ -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'; @@ -123,3 +123,67 @@ export const Validity: Story = { message: 'This field is required, please make a selection', }, }; + +export const FullWidth: Story = { + render: () => ( + <> + +
+ + Full Width + + This is the first choice + + + This is the second choice + + + + 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 + + + + + Full Width with styles + + This is the first choice + + + This is the second choice + + + + 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 + + + +
+ + ), +}; diff --git a/packages/pharos/src/components/radio-button/pharos-radio-button.scss b/packages/pharos/src/components/radio-button/pharos-radio-button.scss index 012c978b8..efb20c2d8 100644 --- a/packages/pharos/src/components/radio-button/pharos-radio-button.scss +++ b/packages/pharos/src/components/radio-button/pharos-radio-button.scss @@ -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; diff --git a/packages/pharos/src/components/radio-button/pharos-radio-button.test.ts b/packages/pharos/src/components/radio-button/pharos-radio-button.test.ts index a3c7a6faf..9ad048485 100644 --- a/packages/pharos/src/components/radio-button/pharos-radio-button.test.ts +++ b/packages/pharos/src/components/radio-button/pharos-radio-button.test.ts @@ -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 radio`, + { 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 radio`, + { 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'); diff --git a/packages/pharos/src/components/radio-button/pharos-radio-button.ts b/packages/pharos/src/components/radio-button/pharos-radio-button.ts index 8fe8c7150..25787b8dd 100644 --- a/packages/pharos/src/components/radio-button/pharos-radio-button.ts +++ b/packages/pharos/src/components/radio-button/pharos-radio-button.ts @@ -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; diff --git a/packages/pharos/src/components/radio-button/pharos-radio-button.wc.stories.ts b/packages/pharos/src/components/radio-button/pharos-radio-button.wc.stories.ts index 112293e75..cfcb00c5d 100644 --- a/packages/pharos/src/components/radio-button/pharos-radio-button.wc.stories.ts +++ b/packages/pharos/src/components/radio-button/pharos-radio-button.wc.stories.ts @@ -102,3 +102,59 @@ export const Validity: Story = { message: 'This field is required, please make a selection', }, }; + +export const FullWidth: Story = { + render: () => + html` +
+ + Full Width + This is the first choice + This is the second choice + 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 + + + Full Width with styles + This is the first choice + This is the second choice + 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 + +
`, +}; diff --git a/packages/pharos/src/components/radio-group/pharos-radio-group.scss b/packages/pharos/src/components/radio-group/pharos-radio-group.scss index 3d1080ae9..5f2fe8857 100644 --- a/packages/pharos/src/components/radio-group/pharos-radio-group.scss +++ b/packages/pharos/src/components/radio-group/pharos-radio-group.scss @@ -6,6 +6,7 @@ .radio-group { border: 0; padding: 0; + width: 100%; } .radio-group__radios {