-
Notifications
You must be signed in to change notification settings - Fork 257
chore(meter): add vrt coverage #6576
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| /** | ||
| * Copyright 2026 Adobe. All rights reserved. | ||
| * This file is licensed to you under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. You may obtain a copy | ||
| * of the License at http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software distributed under | ||
| * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS | ||
| * OF ANY KIND, either express or implied. See the License for the specific language | ||
| * governing permissions and limitations under the License. | ||
| */ | ||
|
|
||
| import { html, nothing } from 'lit'; | ||
| import type { Meta, StoryObj as Story } from '@storybook/web-components'; | ||
|
|
||
| import '@adobe/spectrum-wc/components/meter/swc-meter.js'; | ||
|
|
||
| import type { CustomPropertyCase } from '../../../../.storybook/helpers/index.js'; | ||
| import { | ||
| coveredCustomProperties, | ||
| customPropertyRows, | ||
| theme, | ||
| verifyCustomPropertyCoverage, | ||
| vrtParameters, | ||
| } from '../../../../.storybook/helpers/index.js'; | ||
| import customElementsManifest from '../../../../dist/custom-elements.json'; | ||
|
|
||
| // Metadata | ||
|
|
||
| const meta: Meta = { | ||
| title: 'Meter/Meter VRT', | ||
| component: 'swc-meter', | ||
| tags: ['dev'], | ||
| }; | ||
|
|
||
| export default meta; | ||
|
|
||
| // Helpers | ||
|
|
||
| // Every `--swc-linear-progress-*` custom property documented on | ||
| // `<swc-meter>` (see Meter.ts's `@cssprop` list) is a public contract that | ||
| // `<swc-meter>` shares with the not-yet-migrated `<swc-progress-bar>`. One | ||
| // row per property: a reference meter next to the same meter with that one | ||
| // property overridden to an obviously different value, so a real difference | ||
| // confirms the override still works. | ||
| type MeterPropertyCase = CustomPropertyCase<`--swc-linear-progress-${string}`>; | ||
|
|
||
| const MOD_PROPERTY_CASES: readonly MeterPropertyCase[] = [ | ||
| { property: '--swc-linear-progress-fill-color', value: 'magenta' }, | ||
| { property: '--swc-linear-progress-track-color', value: 'magenta' }, | ||
| { property: '--swc-linear-progress-text-color', value: 'magenta' }, | ||
| { property: '--swc-linear-progress-thickness', value: '40px' }, | ||
| { property: '--swc-linear-progress-font-size', value: '32px' }, | ||
| { property: '--swc-linear-progress-top-to-text', value: '40px' }, | ||
| ]; | ||
|
|
||
| const modPropertyMeter = (_case: MeterPropertyCase, style?: string) => html` | ||
| <swc-meter value="60" style=${style ?? nothing}> | ||
| <span slot="label">Storage used</span> | ||
| </swc-meter> | ||
| `; | ||
|
|
||
| const modPropertiesContent = () => | ||
| customPropertyRows(MOD_PROPERTY_CASES, modPropertyMeter); | ||
|
|
||
| const coveredMeterCustomProperties = | ||
| coveredCustomProperties(MOD_PROPERTY_CASES); | ||
|
|
||
| const verifyCoverage = async () => { | ||
| await verifyCustomPropertyCoverage({ | ||
| customElementsManifest, | ||
| modulePath: 'components/meter/Meter.ts', | ||
| declarationName: 'Meter', | ||
| coveredProperties: coveredMeterCustomProperties, | ||
| }); | ||
| }; | ||
|
|
||
| // VRT stories | ||
|
|
||
| export const CustomProperties: Story = { | ||
| render: () => theme(modPropertiesContent(), 'light', 'ltr'), | ||
| parameters: vrtParameters, | ||
| play: verifyCoverage, | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,273 @@ | ||
| /** | ||
| * Copyright 2026 Adobe. All rights reserved. | ||
| * This file is licensed to you under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. You may obtain a copy | ||
| * of the License at http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software distributed under | ||
| * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS | ||
| * OF ANY KIND, either express or implied. See the License for the specific language | ||
| * governing permissions and limitations under the License. | ||
| */ | ||
|
|
||
| import { html, nothing } from 'lit'; | ||
| import type { Meta, StoryObj as Story } from '@storybook/web-components'; | ||
|
|
||
| import { | ||
| METER_VARIANTS, | ||
| type MeterVariant, | ||
| } from '@adobe/spectrum-wc-core/components/meter'; | ||
| import { | ||
| LINEAR_PROGRESS_LABEL_POSITIONS, | ||
| LINEAR_PROGRESS_STATIC_COLORS, | ||
| LINEAR_PROGRESS_VALID_SIZES, | ||
| type LinearProgressLabelPosition, | ||
| type LinearProgressSize, | ||
| type LinearProgressStaticColor, | ||
| } from '@adobe/spectrum-wc-core/mixins/index.js'; | ||
|
|
||
| import '@adobe/spectrum-wc/components/meter/swc-meter.js'; | ||
|
|
||
| import { | ||
| forcedColorsVrtParameters, | ||
| row, | ||
| staticColorBackground, | ||
| theme, | ||
| vrtParameters, | ||
| } from '../../../../.storybook/helpers/index.js'; | ||
|
|
||
| // Metadata | ||
|
|
||
| const meta: Meta = { | ||
| title: 'Meter/Meter VRT', | ||
| component: 'swc-meter', | ||
| tags: ['dev'], | ||
| }; | ||
|
|
||
| export default meta; | ||
|
|
||
| // Helpers | ||
|
|
||
| const sizeLabels = { | ||
| s: 'Small', | ||
| m: 'Medium', | ||
| l: 'Large', | ||
| xl: 'Extra-large', | ||
| } as const satisfies Record<LinearProgressSize, string>; | ||
|
|
||
| const variantLabels = { | ||
| informative: 'Informative', | ||
| positive: 'Positive', | ||
| notice: 'Notice', | ||
| negative: 'Negative', | ||
| } as const satisfies Record<MeterVariant, string>; | ||
|
|
||
| const labelPositionLabels = { | ||
| top: 'Top label', | ||
| side: 'Side label', | ||
| } as const satisfies Record<LinearProgressLabelPosition, string>; | ||
|
|
||
| const staticColorLabels = { | ||
| white: 'Static white', | ||
| black: 'Static black', | ||
| } as const satisfies Record<LinearProgressStaticColor, string>; | ||
|
|
||
| type MeterCase = { | ||
| size?: LinearProgressSize; | ||
| variant?: MeterVariant; | ||
| value?: number; | ||
| minValue?: number; | ||
| maxValue?: number; | ||
| labelPosition?: LinearProgressLabelPosition; | ||
| staticColor?: LinearProgressStaticColor; | ||
| label?: string; | ||
| description?: string; | ||
| accessibleLabel?: string; | ||
| valueLabel?: string; | ||
| formatOptions?: Intl.NumberFormatOptions; | ||
| lang?: string; | ||
| }; | ||
|
|
||
| // `formatOptions` is a JS-only property (no attribute), so it's set via a | ||
| // `.formatOptions=` property binding rather than an attribute like the rest | ||
| // of these cases. | ||
| const renderMeter = ({ | ||
| size, | ||
| variant, | ||
| value = 50, | ||
| minValue, | ||
| maxValue, | ||
| labelPosition, | ||
| staticColor, | ||
| label, | ||
| description, | ||
| accessibleLabel, | ||
| valueLabel, | ||
| formatOptions, | ||
| lang, | ||
| }: MeterCase) => html` | ||
| <swc-meter | ||
| size=${size ?? nothing} | ||
| variant=${variant ?? nothing} | ||
| value=${value} | ||
| min-value=${minValue ?? nothing} | ||
| max-value=${maxValue ?? nothing} | ||
| label-position=${labelPosition ?? nothing} | ||
| static-color=${staticColor ?? nothing} | ||
| accessible-label=${accessibleLabel ?? nothing} | ||
| value-label=${valueLabel ?? nothing} | ||
| lang=${lang ?? nothing} | ||
| .formatOptions=${formatOptions} | ||
| > | ||
| ${label | ||
| ? html` | ||
| <span slot="label">${label}</span> | ||
| ` | ||
| : nothing} | ||
| ${description | ||
| ? html` | ||
| <span slot="description">${description}</span> | ||
| ` | ||
| : nothing} | ||
| </swc-meter> | ||
| `; | ||
|
|
||
| // Every size and variant (each independent of the other, since size only | ||
| // scales thickness/font-size and variant only swaps the fill color — no | ||
| // interaction effect to cross-check), both label positions, the CSS-visible | ||
| // range edges (0 %, midpoint, 100 %), an over-range value clamped to a full | ||
| // bar while `value-label` still reports the real underlying number, every | ||
| // anatomy combination from the docs Anatomy story (label only, label + | ||
| // description, accessible-label-only fallback, custom value text), the | ||
| // custom-range and format-options behaviors, label wrapping, CJK label | ||
| // rendering (line-height differs under linear-progress-base.css's | ||
| // `:lang(ja/zh/ko)` rule), and static colors on their contrast backgrounds. | ||
| const permutationContent = () => html` | ||
| ${row( | ||
| LINEAR_PROGRESS_VALID_SIZES.map((size) => | ||
| renderMeter({ size, label: sizeLabels[size] }) | ||
| ), | ||
| 'Sizes' | ||
| )} | ||
| ${row( | ||
| METER_VARIANTS.map((variant) => | ||
| renderMeter({ variant, label: variantLabels[variant] }) | ||
| ), | ||
| 'Variants' | ||
| )} | ||
| ${row( | ||
| LINEAR_PROGRESS_LABEL_POSITIONS.map((labelPosition) => | ||
| renderMeter({ | ||
| labelPosition, | ||
| label: labelPositionLabels[labelPosition], | ||
| }) | ||
| ), | ||
| 'Label position' | ||
| )} | ||
| ${row( | ||
| [ | ||
| renderMeter({ value: 0, label: '0%' }), | ||
| renderMeter({ value: 25, label: '25%' }), | ||
| renderMeter({ value: 50, label: '50%' }), | ||
| renderMeter({ value: 75, label: '75%' }), | ||
| renderMeter({ value: 100, label: '100%' }), | ||
| renderMeter({ | ||
| value: 150, | ||
| maxValue: 100, | ||
| label: 'Over 100%', | ||
| valueLabel: '150%', | ||
| }), | ||
| ], | ||
| 'Values' | ||
| )} | ||
| ${row( | ||
| [ | ||
| renderMeter({ value: 40, label: 'Label only' }), | ||
| renderMeter({ | ||
| value: 40, | ||
| label: 'Label and description', | ||
| description: 'Additional context below the bar', | ||
| }), | ||
| renderMeter({ value: 40, accessibleLabel: 'Screen-reader-only label' }), | ||
| renderMeter({ | ||
| value: 40, | ||
| label: 'Custom value text', | ||
| valueLabel: '1 of 4', | ||
| }), | ||
| ], | ||
| 'Anatomy' | ||
| )} | ||
| ${row( | ||
| [ | ||
| renderMeter({ | ||
| minValue: 0, | ||
| maxValue: 10, | ||
| value: 3, | ||
| label: 'Custom range', | ||
| valueLabel: '3 of 10', | ||
| }), | ||
| renderMeter({ | ||
| value: 42, | ||
| label: 'Format options', | ||
| formatOptions: { style: 'currency', currency: 'USD' }, | ||
| }), | ||
| ], | ||
| 'Behaviors' | ||
| )} | ||
| ${row( | ||
| [ | ||
| renderMeter({ | ||
| label: | ||
| 'A label long enough to wrap onto multiple lines within the available inline space', | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed in ba1f188 — constrained the meter to |
||
| }), | ||
| ], | ||
| 'Wrapping' | ||
| )} | ||
| ${row( | ||
| [ | ||
| renderMeter({ lang: 'ja', label: '承認ワークフローの進行状況' }), | ||
| renderMeter({ lang: 'ko', label: '승인 워크플로 진행 상황' }), | ||
| renderMeter({ lang: 'zh', label: '审批工作流进度' }), | ||
| ], | ||
| 'CJK language' | ||
| )} | ||
| ${LINEAR_PROGRESS_STATIC_COLORS.map((staticColor) => | ||
| staticColorBackground( | ||
| row( | ||
| [ | ||
| renderMeter({ staticColor, label: staticColorLabels[staticColor] }), | ||
| renderMeter({ | ||
| staticColor, | ||
| labelPosition: 'side', | ||
| label: `${staticColorLabels[staticColor]} · side label`, | ||
| }), | ||
| ], | ||
| staticColorLabels[staticColor] | ||
| ), | ||
| staticColor | ||
| ) | ||
| )} | ||
| `; | ||
|
|
||
| // VRT stories | ||
|
|
||
| // Rendered once in light/ltr and once in dark/rtl (that combination covers | ||
| // both axes), all still in a single story so it costs one snapshot. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't know that this comment is adding much value in its current state. How would you feel about removing it?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removed in ba1f188. |
||
| export const Permutations: Story = { | ||
| render: () => html` | ||
| ${theme(permutationContent(), 'light', 'ltr')} | ||
| ${theme(permutationContent(), 'dark', 'rtl')} | ||
| `, | ||
| parameters: vrtParameters, | ||
| }; | ||
|
|
||
| // `forced-colors` replaces the whole page palette, so it can't be scoped to | ||
| // a subtree the way theme()'s light/dark split is, and needs its own | ||
| // snapshot rather than folding into Permutations. Confirms the | ||
| // `@media (forced-colors: active)` override in linear-progress-base.css | ||
| // (ButtonFace track, ButtonText fill) applies regardless of variant or | ||
| // static-color. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this comment is adding much value in its current state. How would you feel about removing it?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removed in ba1f188. |
||
| export const ForcedColors: Story = { | ||
| render: () => theme(permutationContent(), 'light', 'ltr'), | ||
| parameters: forcedColorsVrtParameters, | ||
| }; | ||

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if we go, deep-plum instead for all three of these? I checked out deep-magenta, and it still fails for the fill (though the text is great and has the 7:5:1 like you said against white).
Also I don't know that the comment is necessarily needed and or can be condensed. I feel like claude tends to over explain in it's comments.