Skip to content

Commit

Permalink
feat(web-components): refactor progress bar to use ElementInternals (m…
Browse files Browse the repository at this point in the history
…icrosoft#31652)

Co-authored-by: John Kreitlow <[email protected]>
  • Loading branch information
chrisdholt and radium-v authored Jun 11, 2024
1 parent 0026465 commit 7cf5e76
Show file tree
Hide file tree
Showing 11 changed files with 227 additions and 352 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "refactor progress bar to use ElementInternals",
"packageName": "@fluentui/web-components",
"email": "[email protected]",
"dependentChangeType": "patch"
}
30 changes: 20 additions & 10 deletions packages/web-components/docs/api-report.md
Original file line number Diff line number Diff line change
Expand Up @@ -1888,7 +1888,6 @@ export function display(displayValue: CSSDisplayPropertyValue): string;
//
// @public
export class Divider extends FASTElement {
constructor();
// (undocumented)
alignContent?: DividerAlignContent;
// (undocumented)
Expand Down Expand Up @@ -2437,13 +2436,30 @@ export const MenuStyles: ElementStyles;
// @public (undocumented)
export const MenuTemplate: ElementViewTemplate<Menu>;

// Warning: (ae-forgotten-export) The symbol "BaseProgress" needs to be exported by the entry point index.d.ts
//
// @public
export class ProgressBar extends BaseProgress {
export class ProgressBar extends FASTElement {
constructor();
// (undocumented)
connectedCallback(): void;
// @internal
elementInternals: ElementInternals;
// @internal
max?: number;
// (undocumented)
protected maxChanged(): void;
// @internal
min?: number;
// (undocumented)
protected minChanged(): void;
// @internal
percentComplete: number;
shape?: ProgressBarShape;
thickness?: ProgressBarThickness;
validationState: ProgressBarValidationState | null;
// @internal
value?: number | null;
// (undocumented)
protected valueChanged(): void;
}

// @public
Expand Down Expand Up @@ -2485,12 +2501,6 @@ export const ProgressBarValidationState: {
// @public
export type ProgressBarValidationState = ValuesOf<typeof ProgressBarValidationState>;

// @public
export type ProgressOptions = {
indeterminateIndicator1?: StaticallyComposableHTML<ProgressBar>;
indeterminateIndicator2?: StaticallyComposableHTML<ProgressBar>;
};

// Warning: (ae-forgotten-export) The symbol "FormAssociatedRadio" needs to be exported by the entry point index.d.ts
//
// @public
Expand Down
1 change: 0 additions & 1 deletion packages/web-components/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,6 @@ export {
ProgressBarThickness,
ProgressBarValidationState,
} from './progress-bar/index.js';
export type { ProgressOptions } from './progress-bar/index.js';
export {
RadioGroup,
RadioGroupDefinition,
Expand Down
71 changes: 0 additions & 71 deletions packages/web-components/src/progress-bar/base-progress.ts

This file was deleted.

1 change: 0 additions & 1 deletion packages/web-components/src/progress-bar/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
export { definition as ProgressBarDefinition } from './progress-bar.definition.js';
export { ProgressBar } from './progress-bar.js';
export { ProgressBarShape, ProgressBarThickness, ProgressBarValidationState } from './progress-bar.options.js';
export type { ProgressOptions } from './progress-bar.options.js';
export { styles as ProgressBarStyles } from './progress-bar.styles.js';
export { template as ProgressBarTemplate } from './progress-bar.template.js';
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,3 @@ export const ProgressBarValidationState = {
* @public
*/
export type ProgressBarValidationState = ValuesOf<typeof ProgressBarValidationState>;

/**
* Progress configuration options
* @public
*/
export type ProgressOptions = {
indeterminateIndicator1?: StaticallyComposableHTML<ProgressBar>;
indeterminateIndicator2?: StaticallyComposableHTML<ProgressBar>;
};
123 changes: 48 additions & 75 deletions packages/web-components/src/progress-bar/progress-bar.spec.ts
Original file line number Diff line number Diff line change
@@ -1,102 +1,70 @@
import { expect, test } from '@playwright/test';
import type { Locator, Page } from '@playwright/test';
import { fixtureURL } from '../helpers.tests.js';
import type { ProgressBar } from './progress-bar.js';

test.describe('Progress Bar', () => {
let page: Page;
let element: Locator;
let root: Locator;

test.beforeAll(async ({ browser }) => {
page = await browser.newPage();

element = page.locator('fluent-progress-bar');

root = page.locator('#root');

test.beforeEach(async ({ page }) => {
await page.goto(fixtureURL('components-progressbar--progress'));
});

test.afterAll(async () => {
await page.close();
await page.waitForFunction(() => customElements.whenDefined('fluent-progress-bar'));
});

// Foundation tests
test('should include a role of progressbar', async () => {
await expect(element).toHaveAttribute('role', 'progressbar');
});
test('should include a role of progressbar', async ({ page }) => {
const element = page.locator('fluent-progress-bar');

test('should set the `aria-valuenow` attribute with the `value` property when provided', async () => {
await root.evaluate(node => {
node.innerHTML = /* html */ `
<fluent-progress-bar value="50"></fluent-progress-bar>
`;
});

await expect(element).toHaveAttribute('aria-valuenow', '50');
await expect(element).toHaveJSProperty('elementInternals.role', 'progressbar');
});

test('should set the `aria-valuemin` attribute with the `min` property when provided', async () => {
await root.evaluate(node => {
node.innerHTML = /* html */ `
<fluent-progress-bar min="50"></fluent-progress-bar>
`;
});
test('should set the `aria-valuenow` attribute with the `value` property when provided', async ({ page }) => {
const element = page.locator('fluent-progress-bar');

await expect(element).toHaveAttribute('aria-valuemin', '50');
});
await page.setContent(/* html */ `
<fluent-progress-bar value="50"></fluent-progress-bar>
`);

test('should set the `aria-valuemax` attribute with the `max` property when provided', async () => {
await root.evaluate(node => {
node.innerHTML = /* html */ `
<fluent-progress-bar max="50"></fluent-progress-bar>
`;
});

await expect(element).toHaveAttribute('aria-valuemax', '50');
await expect(element).toHaveJSProperty('elementInternals.ariaValueNow', '50');
});

test('should render an element with a `determinate` slot when a value is provided', async () => {
await root.evaluate(node => {
node.innerHTML = /* html */ `
<fluent-progress-bar value="50"></fluent-progress-bar>
`;
});
test('should set the `aria-valuemin` attribute with the `min` property when provided', async ({ page }) => {
const element = page.locator('fluent-progress-bar');

const progress = element.locator('.progress');
await page.setContent(/* html */ `
<fluent-progress-bar min="50"></fluent-progress-bar>
`);

await expect(progress).toHaveAttribute('slot', 'determinate');
await expect(element).toHaveJSProperty('elementInternals.ariaValueMin', '50');
});

test('should render an element with an `indeterminate` slot when no value is provided', async () => {
await root.evaluate(node => {
node.innerHTML = /* html */ `
<fluent-progress-bar></fluent-progress-bar>
`;
});
test('should set the `aria-valuemax` attribute with the `max` property when provided', async ({ page }) => {
const element = page.locator('fluent-progress-bar');

const progress = element.locator('.progress');
await page.setContent(/* html */ `
<fluent-progress-bar max="50"></fluent-progress-bar>
`);

await expect(progress).toHaveAttribute('slot', 'indeterminate');
await expect(element).toHaveJSProperty('elementInternals.ariaValueMax', '50');
});

test('should return the `percentComplete` property as a value between 0 and 100 when `min` and `max` are unset', async () => {
await root.evaluate(node => {
node.innerHTML = /* html */ `
<fluent-progress-bar value="50"></fluent-progress-bar>
`;
});
test('should return the `percentComplete` property as a value between 0 and 100 when `min` and `max` are unset', async ({
page,
}) => {
const element = page.locator('fluent-progress-bar');

await page.setContent(/* html */ `
<fluent-progress-bar value="50"></fluent-progress-bar>
`);

await expect(element).toHaveJSProperty('percentComplete', 50);
});

test('should set the `percentComplete` property to match the current `value` in the range of `min` and `max`', async () => {
await root.evaluate(node => {
node.innerHTML = /* html */ `
<fluent-progress-bar value="0"></fluent-progress-bar>
`;
});
test('should set the `percentComplete` property to match the current `value` in the range of `min` and `max`', async ({
page,
}) => {
const element = page.locator('fluent-progress-bar');

await page.setContent(/* html */ `
<fluent-progress-bar value="0"></fluent-progress-bar>
`);

await expect(element).toHaveJSProperty('percentComplete', 0);

Expand Down Expand Up @@ -125,8 +93,9 @@ test.describe('Progress Bar', () => {
await expect(element).toHaveJSProperty('percentComplete', 0);
});

// Fluent Specific propertiy tests
test('should set and retrieve the `thickness` property correctly', async () => {
test('should set and retrieve the `thickness` property correctly', async ({ page }) => {
const element = page.locator('fluent-progress-bar');

await element.evaluate((node: ProgressBar) => {
node.thickness = 'medium';
});
Expand All @@ -140,7 +109,9 @@ test.describe('Progress Bar', () => {
await expect(element).toHaveJSProperty('thickness', 'large');
});

test('should set and retrieve the `shape` property correctly', async () => {
test('should set and retrieve the `shape` property correctly', async ({ page }) => {
const element = page.locator('fluent-progress-bar');

await element.evaluate((node: ProgressBar) => {
node.shape = 'square';
});
Expand All @@ -154,7 +125,9 @@ test.describe('Progress Bar', () => {
await expect(element).toHaveJSProperty('shape', 'rounded');
});

test('should set and retrieve the `validationState` property correctly', async () => {
test('should set and retrieve the `validationState` property correctly', async ({ page }) => {
const element = page.locator('fluent-progress-bar');

await element.evaluate((node: ProgressBar) => {
node.validationState = 'success';
});
Expand Down
19 changes: 9 additions & 10 deletions packages/web-components/src/progress-bar/progress-bar.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ const storyTemplate = html<ProgressStoryArgs>`
thickness=${x => x.thickness}
shape=${x => x.shape}
max=${x => x.max}
aria-valuemax=${x => x.max}
aria-valuenow=${x => x.value}
min=${x => x.min}
value=${x => x.value}
validation-state=${x => x.validationState}
></fluent-progress-bar>
Expand Down Expand Up @@ -72,26 +71,26 @@ export const Max = renderComponent(html<ProgressStoryArgs>`
<div>
<p>
<code>3 of 10</code>
<fluent-progress-bar value="3" aria-valuenow="3" max="10" aria-valuemax="10"></fluent-progress-bar>
<fluent-progress-bar value="3" max="10"></fluent-progress-bar>
</p>
<p>
<code>3 o 5</code>
<fluent-progress-bar value="3" aria-valuenow="3" max="5" aria-valuemax="5"></fluent-progress-bar>
<code>3 of 5</code>
<fluent-progress-bar value="3" max="5"></fluent-progress-bar>
</p>
</div>
`);

export const Value = renderComponent(html<ProgressStoryArgs>`
<div>
<code>0</code><fluent-progress-bar value="0" aria-valuenow="0"></fluent-progress-bar>
<code>0</code><fluent-progress-bar value="0"></fluent-progress-bar>
<code>25</code>
<fluent-progress-bar value="25" aria-valuenow="25"></fluent-progress-bar>
<fluent-progress-bar value="25"></fluent-progress-bar>
<code>50</code>
<fluent-progress-bar value="50" aria-valuenow="50"></fluent-progress-bar>
<fluent-progress-bar value="50"></fluent-progress-bar>
<code>75</code>
<fluent-progress-bar value="75" aria-valuenow="75"></fluent-progress-bar>
<fluent-progress-bar value="75"></fluent-progress-bar>
<code>100</code>
<fluent-progress-bar value="100" aria-valuenow="100"></fluent-progress-bar>
<fluent-progress-bar value="100"></fluent-progress-bar>
</div>
`);

Expand Down
Loading

0 comments on commit 7cf5e76

Please sign in to comment.