From e52f5587348f94535269f0e7004272cc1e315e00 Mon Sep 17 00:00:00 2001 From: Chris Holt <13071055+chrisdholt@users.noreply.github.com> Date: Tue, 18 Jun 2024 13:32:17 -0700 Subject: [PATCH] feat: update checkbox to use Element Internals custom states for presentational styles --- ...-76101ff6-e6ee-43b5-8c67-f75bd0466480.json | 7 +++ packages/web-components/docs/api-report.md | 4 +- .../src/checkbox/checkbox.spec.ts | 58 +++++++++++++++++-- .../src/checkbox/checkbox.styles.ts | 11 ++-- .../web-components/src/checkbox/checkbox.ts | 30 +++++++++- 5 files changed, 99 insertions(+), 11 deletions(-) create mode 100644 change/@fluentui-web-components-76101ff6-e6ee-43b5-8c67-f75bd0466480.json diff --git a/change/@fluentui-web-components-76101ff6-e6ee-43b5-8c67-f75bd0466480.json b/change/@fluentui-web-components-76101ff6-e6ee-43b5-8c67-f75bd0466480.json new file mode 100644 index 00000000000000..db2c051365938b --- /dev/null +++ b/change/@fluentui-web-components-76101ff6-e6ee-43b5-8c67-f75bd0466480.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "feat: update checkbox to use Element Internals custom states for presentational attributes", + "packageName": "@fluentui/web-components", + "email": "13071055+chrisdholt@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/packages/web-components/docs/api-report.md b/packages/web-components/docs/api-report.md index 7bd0770d3fccf2..ec914aac12d7a6 100644 --- a/packages/web-components/docs/api-report.md +++ b/packages/web-components/docs/api-report.md @@ -590,8 +590,10 @@ export type ButtonType = ValuesOf; // // @public (undocumented) export class Checkbox extends BaseCheckbox { - shape: CheckboxShape; + shape?: CheckboxShape; + shapeChanged(prev: CheckboxShape | undefined, next: CheckboxShape | undefined): void; size?: CheckboxSize; + sizeChanged(prev: CheckboxSize | undefined, next: CheckboxSize | undefined): void; } // @public diff --git a/packages/web-components/src/checkbox/checkbox.spec.ts b/packages/web-components/src/checkbox/checkbox.spec.ts index f75f009558f3a3..adacd38386bbf6 100644 --- a/packages/web-components/src/checkbox/checkbox.spec.ts +++ b/packages/web-components/src/checkbox/checkbox.spec.ts @@ -27,14 +27,36 @@ test.describe('Checkbox', () => { await expect(element).toHaveAttribute('shape', 'square'); }); - test('should set and retrieve the `size` property correctly', async ({ page }) => { + test('should add a custom state matching the `shape` attribute when provided', async ({ page }) => { const element = page.locator('fluent-checkbox'); await page.setContent(/* html */ ` - - `); + + `); + + await element.evaluate((node: Checkbox) => { + node.shape = 'circular'; + }); + + expect(await element.evaluate((node: Checkbox) => node.elementInternals.states.has('circular'))).toBe(true); + + await element.evaluate((node: Checkbox) => { + node.shape = 'square'; + }); + + expect(await element.evaluate((node: Checkbox) => node.elementInternals.states.has('circular'))).toBe(false); + expect(await element.evaluate((node: Checkbox) => node.elementInternals.states.has('square'))).toBe(true); + + await element.evaluate((node: Checkbox) => { + node.shape = undefined; + }); - await expect(element).toHaveJSProperty('size', 'small'); + expect(await element.evaluate((node: Checkbox) => node.elementInternals.states.has('circular'))).toBe(false); + expect(await element.evaluate((node: Checkbox) => node.elementInternals.states.has('square'))).toBe(false); + }); + + test('should set and retrieve the `size` property correctly', async ({ page }) => { + const element = page.locator('fluent-checkbox'); await element.evaluate((node: Checkbox) => { node.size = 'medium'; @@ -49,6 +71,34 @@ test.describe('Checkbox', () => { await expect(element).toHaveJSProperty('size', 'large'); }); + test('should add a custom state matching the `size` attribute when provided', async ({ page }) => { + const element = page.locator('fluent-checkbox'); + + await page.setContent(/* html */ ` + + `); + + await element.evaluate((node: Checkbox) => { + node.size = 'medium'; + }); + + expect(await element.evaluate((node: Checkbox) => node.elementInternals.states.has('medium'))).toBe(true); + + await element.evaluate((node: Checkbox) => { + node.size = 'large'; + }); + + expect(await element.evaluate((node: Checkbox) => node.elementInternals.states.has('medium'))).toBe(false); + expect(await element.evaluate((node: Checkbox) => node.elementInternals.states.has('large'))).toBe(true); + + await element.evaluate((node: Checkbox) => { + node.size = undefined; + }); + + expect(await element.evaluate((node: Checkbox) => node.elementInternals.states.has('medium'))).toBe(false); + expect(await element.evaluate((node: Checkbox) => node.elementInternals.states.has('large'))).toBe(false); + }); + test('should have a role of `checkbox`', async ({ page }) => { const element = page.locator('fluent-checkbox'); diff --git a/packages/web-components/src/checkbox/checkbox.styles.ts b/packages/web-components/src/checkbox/checkbox.styles.ts index 756ea02a1ba3c2..9b1abfb1082318 100644 --- a/packages/web-components/src/checkbox/checkbox.styles.ts +++ b/packages/web-components/src/checkbox/checkbox.styles.ts @@ -23,6 +23,7 @@ import { } from '../theme/design-tokens.js'; import { forcedColorsStylesheetBehavior } from '../utils/behaviors/match-media-stylesheet-behavior.js'; import { display } from '../utils/display.js'; +import { circularState, largeState } from '../styles/states/index.js'; /** * Selector for the `checked` state. @@ -133,17 +134,17 @@ export const styles = css` inset: 0; } - :host([size='large']) { + :host(${largeState}) { --size: 20px; } - :host([size='large']) ::slotted([slot='checked-indicator']), - :host([size='large']) .checked-indicator { + :host(${largeState}) ::slotted([slot='checked-indicator']), + :host(${largeState}) .checked-indicator { width: 16px; } - :host([shape='circular']), - :host([shape='circular']) .indeterminate-indicator { + :host(${circularState}), + :host(${circularState}) .indeterminate-indicator { border-radius: ${borderRadiusCircular}; } diff --git a/packages/web-components/src/checkbox/checkbox.ts b/packages/web-components/src/checkbox/checkbox.ts index 91de2ababfe2f9..a309a0fb4a44ce 100644 --- a/packages/web-components/src/checkbox/checkbox.ts +++ b/packages/web-components/src/checkbox/checkbox.ts @@ -467,7 +467,21 @@ export class Checkbox extends BaseCheckbox { * HTML Attribute: `shape` */ @attr - public shape!: CheckboxShape; + public shape?: CheckboxShape; + + /** + * Handles changes to shape attribute custom states + * @param prev - the previous state + * @param next - the next state + */ + public shapeChanged(prev: CheckboxShape | undefined, next: CheckboxShape | undefined) { + if (prev) { + toggleState(this.elementInternals, `${prev}`, false); + } + if (next) { + toggleState(this.elementInternals, `${next}`, true); + } + } /** * Indicates the size of the checkbox. @@ -478,4 +492,18 @@ export class Checkbox extends BaseCheckbox { */ @attr public size?: CheckboxSize; + + /** + * Handles changes to size attribute custom states + * @param prev - the previous state + * @param next - the next state + */ + public sizeChanged(prev: CheckboxSize | undefined, next: CheckboxSize | undefined) { + if (prev) { + toggleState(this.elementInternals, `${prev}`, false); + } + if (next) { + toggleState(this.elementInternals, `${next}`, true); + } + } }